From e098670c6c03dcbdcd6e4598553227780a871adb Mon Sep 17 00:00:00 2001 From: Tony Ko Date: Tue, 28 Jul 2026 23:51:27 -0400 Subject: [PATCH 1/4] feat: use local skills --- .agents/skills | 1 + .claude/skills | 1 + 2 files changed, 2 insertions(+) create mode 120000 .agents/skills create mode 120000 .claude/skills diff --git a/.agents/skills b/.agents/skills new file mode 120000 index 0000000..d883408 --- /dev/null +++ b/.agents/skills @@ -0,0 +1 @@ +/Users/xuser/notes/tkodev/harness-skills \ No newline at end of file diff --git a/.claude/skills b/.claude/skills new file mode 120000 index 0000000..d883408 --- /dev/null +++ b/.claude/skills @@ -0,0 +1 @@ +/Users/xuser/notes/tkodev/harness-skills \ No newline at end of file From 77a4acd843e3120b2cfe4f6999305b3173e5471b Mon Sep 17 00:00:00 2001 From: Tony Ko Date: Wed, 29 Jul 2026 00:24:44 -0400 Subject: [PATCH 2/4] docs(prd): scope readout rule to exempt decorative HUD ornament Instrument chrome (dial ticks, gauge scales, reticle telemetry blocks) can now be atmosphere rather than a readout, as long as it never claims a specific real quantity: needed room to build denser HUD-style reticles without every tick or panel number having to be backed by real data. The carve-out is scoped narrowly so it doesn't excuse the counters, dates, and stats already shipped elsewhere on the site, which stay real. --- docs/02-prd/01-brief.md | 19 ++++++++++++------- docs/02-prd/02-observations.md | 13 ++++++++----- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/docs/02-prd/01-brief.md b/docs/02-prd/01-brief.md index 9e221b2..bdd5ac4 100644 --- a/docs/02-prd/01-brief.md +++ b/docs/02-prd/01-brief.md @@ -43,13 +43,18 @@ declassified engineering dossier presents evidence: annotated, indexed, precise. Two pillars, both non-negotiable: 1. **On-brand, and honest where it speaks.** The HUD layer has two tiers. A - *readout* (anything displaying a value: a number, date, coordinate, count, - progress) is always true, sourced from the career data, runtime, or the build. - *Atmosphere* (reticles, rings, scanlines, fields) needs no data behind it; - it needs craft and brand coherence: drawn in the system's language, deliberate, - in service of the feeling. Precision instead of theatrics: the failure mode is - not ornament, it's ornament that feels cheap, and a fake readout is the worst - case because it's both. + *readout* (a value presented as describing something real: career data, site + state, runtime, or the build: a stat, a date, a counter, a coordinate tied to + an actual place) is always true. *Atmosphere* (reticles, rings, scanlines, + fields, dial faces) needs no data behind it; it needs craft and brand + coherence: drawn in the system's language, deliberate, in service of the + feeling. This includes instrument furniture that *looks* numeric (dial + tick scales, HUD gauge chrome, reticle telemetry blocks) as long as it never + claims to represent a specific real quantity: it's the printed face of the + instrument, not a measurement. Precision instead of theatrics: the failure + mode is not ornament, it's ornament that feels cheap, and a readout claiming + to be a real, specific quantity while being fake is the worst case because + it's both. 2. **The experience is futuristic.** Boot sequence, darkness with density at the edges, media that plays itself. The COD MW2 (original) loading-screen energy: information-dense, atmospheric, alive. diff --git a/docs/02-prd/02-observations.md b/docs/02-prd/02-observations.md index 6c5b39a..73e3e38 100644 --- a/docs/02-prd/02-observations.md +++ b/docs/02-prd/02-observations.md @@ -29,11 +29,14 @@ argument against cheap ornament. **Direction.** The two-tier readout/atmosphere rule ([01-brief.md](01-brief.md) pillar 1). Applied concretely: the clock is the real time, the coordinates are Toronto's, the counters derive from the career constants, figure numbers actually -increment, the version stamp is the real build; one obviously-fake readout poisons -trust in all the rest. What gets cut is not ornament but *off-brand* ornament: the -element that could appear on any dark-dashboard Dribbble shot. Precision instead of -theatrics is what separates acustable.com from that pile; feeling instead of -sterility is what separates it from an admin panel. +increment, the version stamp is the real build; one obviously-fake readout claiming +to be a real, specific quantity poisons trust in all the rest. Instrument chrome +that plays a numeric HUD (a dial's tick scale, a reticle's telemetry-block filler) +is atmosphere, not a readout, as long as it never claims a specific real quantity; +it does not need to be true, only on-brand. What gets cut is not ornament but +*off-brand* ornament: the element that could appear on any dark-dashboard Dribbble +shot. Precision instead of theatrics is what separates acustable.com from that +pile; feeling instead of sterility is what separates it from an admin panel. ### Risk: chrome outshining work From d3a0a30dbbec6246e9cdde9b5410407e43a43f82 Mon Sep 17 00:00:00 2001 From: Tony Ko Date: Wed, 29 Jul 2026 00:25:00 -0400 Subject: [PATCH 3/4] feat(reticle): add tick-dial reticle variant for the design specimen hero New ReticleDial atom: a denser HUD instrument face (tick ring, flanking arc brackets, ladder crosshair, side-rail rulers, corner registration marks) alongside the existing branded Reticle, which is untouched. Wired into the /design hero only, with real BUILD/ZONE readouts sourced from package.json and the app timezone constant. Adds a shared --animate-drift token to theme.css for the dial's slow tick rotation, following the existing marquee/ticker token pattern. --- src/components/atoms/reticle-dial.tsx | 311 +++++++++++++++++++++++ src/components/atoms/reticle.tsx | 1 + src/components/organisms/design-hero.tsx | 12 +- src/themes/theme.css | 10 + 4 files changed, 332 insertions(+), 2 deletions(-) create mode 100644 src/components/atoms/reticle-dial.tsx diff --git a/src/components/atoms/reticle-dial.tsx b/src/components/atoms/reticle-dial.tsx new file mode 100644 index 0000000..5588940 --- /dev/null +++ b/src/components/atoms/reticle-dial.tsx @@ -0,0 +1,311 @@ +import { forwardRef, type SVGAttributes } from 'react' +import { cn, cva, type VariantProps } from '@/utils/theme' + +const styles = { + root: cva('text-muted-foreground aspect-square overflow-visible'), + ring: cva('svg-origin animate-drift origin-center motion-reduce:animate-none'), + readoutLabel: cva('text-e4 font-expressive fill-muted-foreground uppercase'), + readoutValue: cva('text-e4 font-expressive fill-foreground uppercase') +} + +type ReticleDialReadout = { + /** Small caps label above the value, e.g. a designation or unit. */ + label: string + /** The real value the label describes; never a placeholder. */ + value: string +} + +type ReticleDialRef = SVGSVGElement +type ReticleDialProps = SVGAttributes & + VariantProps & { + /** Boxed readouts on the side rails: index 0 left, index 1 right. Real data only. */ + readouts?: ReticleDialReadout[] + } + +const size = 896 +const cx = size / 2 +const cy = size / 2 + +/** Point on a circle of the given radius, angle in degrees (0 = right, -90 = up). */ +const point = (angle: number, radius: number) => { + const rad = (angle * Math.PI) / 180 + return { x: cx + radius * Math.cos(rad), y: cy + radius * Math.sin(rad) } +} + +/** SVG arc path between two angles on a circle of the given radius. */ +const arcPath = (radius: number, angleFrom: number, angleTo: number) => { + const start = point(angleFrom, radius) + const end = point(angleTo, radius) + const largeArc = Math.abs(angleTo - angleFrom) > 180 ? 1 : 0 + return `M${start.x} ${start.y} A${radius} ${radius} 0 ${largeArc} 1 ${end.x} ${end.y}` +} + +// closer to the original reticle's second-largest ring (r347.5 in an 896-tall viewBox) +const circleRadius = 340 +const bracketRadius = circleRadius + 40 +const railOffset = bracketRadius + 45 + +// fine spokes just inside the main circle, facing inward like inner dial gauge marks +const fineTickOuter = circleRadius - 8 +const fineTicks = Array.from({ length: 72 }, (_, i) => { + const angle = i * 5 + const long = i % 6 === 0 + const inner = fineTickOuter - (long ? 12 : 6) + const from = point(angle, fineTickOuter) + const to = point(angle, inner) + return { key: angle, long, x1: from.x, y1: from.y, x2: to.x, y2: to.y } +}) + +// texture ticks along the flanking arc brackets +const bracketTicks = [ + ...Array.from({ length: 7 }, (_, i) => -55 + i * (110 / 6)), + ...Array.from({ length: 7 }, (_, i) => 125 + i * (110 / 6)) +].map((angle) => { + const from = point(angle, bracketRadius - 6) + const to = point(angle, bracketRadius + 6) + return { key: angle, x1: from.x, y1: from.y, x2: to.x, y2: to.y } +}) + +const railTicks = Array.from({ length: 9 }, (_, i) => { + const y = cy - 176 + i * 44 + const long = i % 2 === 0 + return { key: i, y, width: long ? 22 : 12 } +}) + +// multi-rung ladder lines flanking the crosshair, mirrored left/right +const ladderRungs = [-92, -46, 46, 92].map((offset) => ({ + key: offset, + y: cy + offset, + width: offset === -46 || offset === 46 ? 44 : 30 +})) + +const cornerMarks = [ + { x: 96, y: 96 }, + { x: size - 96, y: 96 }, + { x: 96, y: size - 96 }, + { x: size - 96, y: size - 96 } +] + +/** + * Fixed decorative HUD panel text: dial furniture, not a readout. Per the + * brief's readout/atmosphere rule ([01-brief.md](../../../docs/02-prd/01-brief.md) + * pillar 1), instrument chrome that never claims a specific real quantity + * is atmosphere, so these values are static and do not need to be true. + */ +const panelReadouts = [ + { + label: 'Drift', + value: '1.0', + x: cx - railOffset - 18, + y: cy - 220, + textAnchor: 'end' as const + }, + { + label: 'Gain', + value: '5.7', + x: cx + railOffset + 18, + y: cy - 220, + textAnchor: 'start' as const + }, + { + label: 'Trim', + value: '0.52', + x: cx - railOffset - 18, + y: cy + 240, + textAnchor: 'end' as const + }, + { + label: 'Scale', + value: '2.485', + x: cx + railOffset + 18, + y: cy + 240, + textAnchor: 'start' as const + } +] + +const ReticleDial = forwardRef((props, ref) => { + // props + const { className, readouts, ...rest } = props + + // render vars + const opacity = 0.3 + const topChevron = point(-90, circleRadius - 18) + const topChevronTip = point(-90, circleRadius - 40) + // mirrored across the center horizon: same shape, flipped vertically + const bottomChevron = point(90, circleRadius - 18) + const bottomChevronTip = point(90, circleRadius - 40) + const railAnchors: { x: number; textAnchor: 'start' | 'end' }[] = [ + { x: cx - railOffset - 18, textAnchor: 'end' }, + { x: cx + railOffset + 18, textAnchor: 'start' } + ] + + // jsx + return ( + + ) +}) +ReticleDial.displayName = 'ReticleDial' + +export { ReticleDial } +export type { ReticleDialProps, ReticleDialRef } diff --git a/src/components/atoms/reticle.tsx b/src/components/atoms/reticle.tsx index b504c92..ab2f7c9 100644 --- a/src/components/atoms/reticle.tsx +++ b/src/components/atoms/reticle.tsx @@ -61,6 +61,7 @@ const Reticle = forwardRef((props, ref) => { {...rest} > + diff --git a/src/components/organisms/design-hero.tsx b/src/components/organisms/design-hero.tsx index 5514010..fd5f8a1 100644 --- a/src/components/organisms/design-hero.tsx +++ b/src/components/organisms/design-hero.tsx @@ -1,8 +1,10 @@ import { forwardRef, type HTMLAttributes } from 'react' import { Hud, type HudProps } from '@/components/atoms/hud' import { Intro, type IntroProps } from '@/components/atoms/intro' -import { Reticle } from '@/components/atoms/reticle' +import { ReticleDial } from '@/components/atoms/reticle-dial' +import { appTimeZone } from '@/constants/date' import { cn, cva, type VariantProps } from '@/utils/theme' +import pkg from '../../../package.json' const styles = { root: cva(['size-full', 'relative overflow-visible']), @@ -24,7 +26,13 @@ const DesignHero = forwardRef((props, ref) => { // jsx return (
- + {children} diff --git a/src/themes/theme.css b/src/themes/theme.css index c50a37b..c7e06f3 100644 --- a/src/themes/theme.css +++ b/src/themes/theme.css @@ -212,6 +212,7 @@ --animate-cursor-wave: cursor-wave 0.6s var(--brand-ease-out) forwards; --animate-marquee: marquee 80s linear infinite; --animate-ticker: ticker 8s linear infinite alternate; + --animate-drift: drift 240s linear infinite; } /* animations */ @@ -242,6 +243,15 @@ } } +@keyframes drift { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + @keyframes ticker { 0%, 15% { From 00ad19a9ffaecee8f3a77f5dc876ae04fa64c0f4 Mon Sep 17 00:00:00 2001 From: Tony Ko Date: Wed, 29 Jul 2026 00:59:18 -0400 Subject: [PATCH 4/4] feat(reticle): roll out ReticleDial across all heroes Replaces the branded Reticle with ReticleDial on home, about, experience, and error, matching the existing /design usage. Every hero now accepts an optional readouts prop (real data only, threaded to ReticleDial's boxed rail readouts); none of the call sites populate it yet, so the heroes render without boxed readouts by default. Also tunes the dial itself: larger circular geometry closer to the original reticle's proportions, a CSS boot-in (nth-child stagger, capped total sequence) replacing the earlier framer-motion pass, symmetric top/bottom panel-readout spacing, and text sizing matched to Hud's expressive style. --- src/components/atoms/reticle-dial.tsx | 303 ++++++++++--------- src/components/organisms/about-hero.tsx | 8 +- src/components/organisms/design-hero.tsx | 4 +- src/components/organisms/error-hero.tsx | 8 +- src/components/organisms/experience-hero.tsx | 8 +- src/components/organisms/home-hero.tsx | 8 +- src/themes/theme.css | 14 + 7 files changed, 203 insertions(+), 150 deletions(-) diff --git a/src/components/atoms/reticle-dial.tsx b/src/components/atoms/reticle-dial.tsx index 5588940..e03f01b 100644 --- a/src/components/atoms/reticle-dial.tsx +++ b/src/components/atoms/reticle-dial.tsx @@ -5,7 +5,22 @@ const styles = { root: cva('text-muted-foreground aspect-square overflow-visible'), ring: cva('svg-origin animate-drift origin-center motion-reduce:animate-none'), readoutLabel: cva('text-e4 font-expressive fill-muted-foreground uppercase'), - readoutValue: cva('text-e4 font-expressive fill-foreground uppercase') + readoutValue: cva('text-e4 font-expressive fill-foreground uppercase'), + panelGroup: cva('opacity-0 transition-opacity duration-1000 xl:opacity-70'), + readoutGroup: cva('opacity-0 transition-opacity duration-1000 xl:opacity-80'), + // boot-in, staggered per top-level child; last delay (500ms) + the 1.5s boot-in-blur + // duration caps the total sequence at 2s + bootGroup: cva([ + '*:svg-origin *:origin-center', + '*:animate-boot-in-blur *:fill-mode-both motion-reduce:*:animate-none', + '[&>*:nth-child(2)]:[animation-delay:50ms]', + '[&>*:nth-child(3)]:[animation-delay:100ms]', + '[&>*:nth-child(4)]:[animation-delay:200ms]', + '[&>*:nth-child(5)]:[animation-delay:400ms]', + '[&>*:nth-child(6)]:[animation-delay:600ms]', + '[&>*:nth-child(7)]:[animation-delay:800ms]', + '[&>*:nth-child(n+8)]:[animation-delay:1200ms]' + ]) } type ReticleDialReadout = { @@ -40,8 +55,8 @@ const arcPath = (radius: number, angleFrom: number, angleTo: number) => { return `M${start.x} ${start.y} A${radius} ${radius} 0 ${largeArc} 1 ${end.x} ${end.y}` } -// closer to the original reticle's second-largest ring (r347.5 in an 896-tall viewBox) -const circleRadius = 340 +// fills the frame the way the original reticle's outer ring (r447.5) did in its 896-tall viewBox +const circleRadius = 378 const bracketRadius = circleRadius + 40 const railOffset = bracketRadius + 45 @@ -97,28 +112,28 @@ const panelReadouts = [ label: 'Drift', value: '1.0', x: cx - railOffset - 18, - y: cy - 220, + y: cy - 207, textAnchor: 'end' as const }, { label: 'Gain', value: '5.7', x: cx + railOffset + 18, - y: cy - 220, + y: cy - 207, textAnchor: 'start' as const }, { label: 'Trim', value: '0.52', x: cx - railOffset - 18, - y: cy + 240, + y: cy + 207, textAnchor: 'end' as const }, { label: 'Scale', value: '2.485', x: cx + railOffset + 18, - y: cy + 240, + y: cy + 207, textAnchor: 'start' as const } ] @@ -153,159 +168,175 @@ const ReticleDial = forwardRef((props, ref) => {...rest} > - {/* main circle */} - - + + {/* main circle */} + + - {/* inset thick ring, same weight as the original reticle's second-largest ring */} - + {/* inset thick ring, same weight as the original reticle's second-largest ring */} + - {/* fine dial texture ring */} - - {fineTicks.map((tick) => ( - - ))} - - - {/* flanking arc brackets */} - - - - {bracketTicks.map((tick) => ( - - ))} - - - {/* chevrons on the main circle, mirrored across the center horizon */} - - + {/* the original reticle's two smaller fine-line inner circles, at their original size */} + + - {/* crosshair ladder */} - - {ladderRungs.map((rung) => ( - - + {/* fine dial texture ring */} + + {fineTicks.map((tick) => ( - - ))} - - - + ))} + - {/* side rail rulers */} - {[cx - railOffset, cx + railOffset].map((railX) => ( - - - {railTicks.map((tick) => ( + {/* flanking arc brackets */} + + + + {bracketTicks.map((tick) => ( ))} - ))} - {/* corner registration marks */} - {cornerMarks.map((corner) => ( - - ))} + + + {/* crosshair ladder */} + + {ladderRungs.map((rung) => ( + + + + + ))} + + + + + {/* side rail rulers */} + + {[cx - railOffset, cx + railOffset].map((railX) => ( + + + {railTicks.map((tick) => ( + + ))} + + ))} + + + {/* corner registration marks */} + + {cornerMarks.map((corner) => ( + + ))} + - {/* fixed HUD panel furniture: decorative, not a readout */} - {panelReadouts.map((panel) => ( - - - {panel.label} - - - {panel.value} - + {/* fixed HUD panel furniture: decorative, not a readout */} + + {panelReadouts.map((panel) => ( + + + {panel.label} + + + {panel.value} + + + ))} - ))} + {/* boxed readouts on the rails: real data only, nothing renders without it */} - {readouts?.slice(0, 2).map((readout, i) => { - const anchor = railAnchors[i] - return ( - - - {readout.label} - - - {readout.value} - - - ) - })} + {!!readouts?.length && ( + + {readouts.slice(0, 2).map((readout, i) => { + const anchor = railAnchors[i] + return ( + + + {readout.label} + + + {readout.value} + + + ) + })} + + )} ) }) ReticleDial.displayName = 'ReticleDial' export { ReticleDial } -export type { ReticleDialProps, ReticleDialRef } +export type { ReticleDialProps, ReticleDialReadout, ReticleDialRef } diff --git a/src/components/organisms/about-hero.tsx b/src/components/organisms/about-hero.tsx index 3b4054f..fb435b6 100644 --- a/src/components/organisms/about-hero.tsx +++ b/src/components/organisms/about-hero.tsx @@ -1,7 +1,7 @@ import { forwardRef, type HTMLAttributes } from 'react' import { Hud, type HudProps } from '@/components/atoms/hud' import { Intro, type IntroProps } from '@/components/atoms/intro' -import { Reticle } from '@/components/atoms/reticle' +import { ReticleDial, type ReticleDialReadout } from '@/components/atoms/reticle-dial' import { cn, cva, type VariantProps } from '@/utils/theme' const styles = { @@ -15,16 +15,18 @@ type AboutHeroProps = HTMLAttributes & VariantProps & { hudProps: HudProps introProps: IntroProps + /** Boxed readouts on the reticle's side rails. Real data only. */ + readouts?: ReticleDialReadout[] } const AboutHero = forwardRef((props, ref) => { // props - const { hudProps, introProps, children, className, ...rest } = props + const { hudProps, introProps, readouts, children, className, ...rest } = props // jsx return (
- + {children} diff --git a/src/components/organisms/design-hero.tsx b/src/components/organisms/design-hero.tsx index fd5f8a1..41c0c3b 100644 --- a/src/components/organisms/design-hero.tsx +++ b/src/components/organisms/design-hero.tsx @@ -3,8 +3,8 @@ import { Hud, type HudProps } from '@/components/atoms/hud' import { Intro, type IntroProps } from '@/components/atoms/intro' import { ReticleDial } from '@/components/atoms/reticle-dial' import { appTimeZone } from '@/constants/date' +import { projectEntries } from '@/constants/projects' import { cn, cva, type VariantProps } from '@/utils/theme' -import pkg from '../../../package.json' const styles = { root: cva(['size-full', 'relative overflow-visible']), @@ -29,7 +29,7 @@ const DesignHero = forwardRef((props, ref) => { diff --git a/src/components/organisms/error-hero.tsx b/src/components/organisms/error-hero.tsx index ce5775f..965a963 100644 --- a/src/components/organisms/error-hero.tsx +++ b/src/components/organisms/error-hero.tsx @@ -1,7 +1,7 @@ import { forwardRef, type HTMLAttributes, type ReactNode } from 'react' import { Hud, type HudProps } from '@/components/atoms/hud' import { Intro, type IntroProps } from '@/components/atoms/intro' -import { Reticle } from '@/components/atoms/reticle' +import { ReticleDial, type ReticleDialReadout } from '@/components/atoms/reticle-dial' import { cn, cva, type VariantProps } from '@/utils/theme' const styles = { @@ -15,17 +15,19 @@ type ErrorHeroProps = HTMLAttributes & VariantProps & { hudProps: HudProps introProps: IntroProps + /** Boxed readouts on the reticle's side rails. Real data only. */ + readouts?: ReticleDialReadout[] children?: ReactNode } const ErrorHero = forwardRef((props, ref) => { // props - const { hudProps, introProps, children, className, ...rest } = props + const { hudProps, introProps, readouts, children, className, ...rest } = props // jsx return (
- + {children} diff --git a/src/components/organisms/experience-hero.tsx b/src/components/organisms/experience-hero.tsx index cd6b963..f3f8968 100644 --- a/src/components/organisms/experience-hero.tsx +++ b/src/components/organisms/experience-hero.tsx @@ -1,7 +1,7 @@ import { forwardRef, type HTMLAttributes } from 'react' import { Hud, type HudProps } from '@/components/atoms/hud' import { Intro, type IntroProps } from '@/components/atoms/intro' -import { Reticle } from '@/components/atoms/reticle' +import { ReticleDial, type ReticleDialReadout } from '@/components/atoms/reticle-dial' import { cn, cva, type VariantProps } from '@/utils/theme' const styles = { @@ -15,16 +15,18 @@ type ExperienceHeroProps = HTMLAttributes & VariantProps & { hudProps: HudProps introProps: IntroProps + /** Boxed readouts on the reticle's side rails. Real data only. */ + readouts?: ReticleDialReadout[] } const ExperienceHero = forwardRef((props, ref) => { // props - const { hudProps, introProps, children, className, ...rest } = props + const { hudProps, introProps, readouts, children, className, ...rest } = props // jsx return (
- + {children} diff --git a/src/components/organisms/home-hero.tsx b/src/components/organisms/home-hero.tsx index 537388c..9b30fd2 100644 --- a/src/components/organisms/home-hero.tsx +++ b/src/components/organisms/home-hero.tsx @@ -1,7 +1,7 @@ import { forwardRef, type HTMLAttributes } from 'react' import { Hud, type HudProps } from '@/components/atoms/hud' import { Intro, type IntroProps } from '@/components/atoms/intro' -import { Reticle } from '@/components/atoms/reticle' +import { ReticleDial, type ReticleDialReadout } from '@/components/atoms/reticle-dial' import { cn, cva, type VariantProps } from '@/utils/theme' const styles = { @@ -15,16 +15,18 @@ type HomeHeroProps = HTMLAttributes & VariantProps & { hudProps: HudProps introProps: IntroProps + /** Boxed readouts on the reticle's side rails. Real data only. */ + readouts?: ReticleDialReadout[] } const HomeHero = forwardRef((props, ref) => { // props - const { hudProps, introProps, children, className, ...rest } = props + const { hudProps, introProps, readouts, children, className, ...rest } = props // jsx return (
- + {children} diff --git a/src/themes/theme.css b/src/themes/theme.css index c7e06f3..47e27b7 100644 --- a/src/themes/theme.css +++ b/src/themes/theme.css @@ -206,6 +206,7 @@ --animate-boot-down: boot-down 1.5s var(--brand-ease-out); --animate-boot-up: boot-up 1.5s var(--brand-ease-out); --animate-boot-in: boot-in 1.5s var(--brand-ease-out); + --animate-boot-in-blur: boot-in-blur 1.5s var(--brand-ease-out); --animate-boot-out: boot-out 1.5s var(--brand-ease-out); --animate-fade-in: fade-in 1.5s var(--brand-ease-out); --animate-fade-out: fade-out 1.5s var(--brand-ease-out); @@ -340,6 +341,19 @@ } } +@keyframes boot-in-blur { + from { + transform: scale(0.85); + filter: blur(8px); + opacity: 0; + } + to { + transform: scale(1); + filter: blur(0px); + opacity: 1; + } +} + @keyframes boot-out { from { transform: scale(1);