diff --git a/.claude/skills/streamplace-design/SKILL.md b/.claude/skills/streamplace-design/SKILL.md index efbb2d625..4d62f3fbd 100644 --- a/.claude/skills/streamplace-design/SKILL.md +++ b/.claude/skills/streamplace-design/SKILL.md @@ -112,18 +112,18 @@ Secondary: teal (`colors.secondary`, the web's `--secondary`/`--accent`). ## Typography For typography sizes, use `typeScale`: sizes -12/13/14/16/20/24/32, line heights in the token file, tight letter-spacing -from 20px up: +14/15/16/18/22/26/34, line heights in the token file, tight letter-spacing +from 22px up: | Key | Size/Line | Weight | Use | | ------ | ---------- | ------ | ------------------------------------- | -| `xs` | 12/16 | 400 | Badges, timestamps, overlines | -| `sm` | 13/18 | 400 | Chat messages, dense metadata | -| `base` | 14/20 | 400 | Default UI text | -| `md` | 16/24 | 400 | Stream titles (rows), emphasized body | -| `lg` | 20/26 −0.2 | 500 | Section headings | -| `xl` | 24/30 −0.3 | 600 | Page titles | -| `xxl` | 32/38 −0.5 | 600 | Hero moments only | +| `xs` | 14/18 | 400 | Badges, timestamps, overlines | +| `sm` | 15/20 | 400 | Chat messages, dense metadata | +| `base` | 16/22 | 400 | Default UI text | +| `md` | 18/26 | 400 | Stream titles (rows), emphasized body | +| `lg` | 22/28 −0.2 | 500 | Section headings | +| `xl` | 26/32 −0.3 | 600 | Page titles | +| `xxl` | 34/40 −0.5 | 600 | Hero moments only | - **Counts, timers, and durations always use `tabularNums`** (`fontVariant: ["tabular-nums"]`) so digits do not jitter. diff --git a/js/components/src/components/ui/input.tsx b/js/components/src/components/ui/input.tsx index 891923e11..a21d7685b 100644 --- a/js/components/src/components/ui/input.tsx +++ b/js/components/src/components/ui/input.tsx @@ -107,8 +107,8 @@ export const Input = forwardRef( const textStyles = React.useMemo(() => { const baseTextStyle = [zt.text.text1, zt.bg.transparent]; - // Inputs never drop below 16px (typeScale.md) — smaller sizes trigger - // zoom-on-focus in mobile web browsers. + // Inputs stay at or above 16px so mobile web browsers don't zoom on + // focus. typeScale.md (18px) clears that floor. switch (size) { case "sm": return [ diff --git a/js/components/src/components/ui/primitives/input.tsx b/js/components/src/components/ui/primitives/input.tsx index 41e6b63da..fd5c6f09c 100644 --- a/js/components/src/components/ui/primitives/input.tsx +++ b/js/components/src/components/ui/primitives/input.tsx @@ -372,8 +372,8 @@ const structuralStyles = StyleSheet.create({ minHeight: tokens.touchTargets.minimum, paddingHorizontal: tokens.spacing[3], paddingVertical: tokens.spacing[2], - // 16px (typeScale.md), not base — inputs under 16px trigger zoom-on-focus - // in mobile web browsers + // typeScale.md, not base — inputs below 16px trigger zoom-on-focus in + // mobile web browsers, and md clears that floor at 18px fontSize: tokens.typeScale.md.fontSize, borderWidth: 1, borderRadius: tokens.borderRadius.md, diff --git a/js/components/src/components/ui/primitives/text.tsx b/js/components/src/components/ui/primitives/text.tsx index b551aac0c..fc04cad22 100644 --- a/js/components/src/components/ui/primitives/text.tsx +++ b/js/components/src/components/ui/primitives/text.tsx @@ -12,6 +12,7 @@ import { useTheme } from "../../../lib/theme/theme"; import { fontFamilies, tabularNums, + typeScale, typography, } from "../../../lib/theme/tokens"; @@ -123,29 +124,30 @@ export interface TextPrimitiveProps extends Omit { reset?: boolean; } -// Size mapping — the modular scale (12/13/14/16/20/24/32). Keys are kept -// stable; "4xl" is a deprecated alias of "3xl". +// Size mapping — derived from `typeScale` so there is a single source of truth +// for the modular scale. Text's keys are offset by one step from `typeScale` +// (Text "lg" is `typeScale.md`, and so on); "4xl" is a deprecated alias of "3xl". const sizeMap = { - xs: 12, - sm: 13, - base: 14, - lg: 16, - xl: 20, - "2xl": 24, - "3xl": 32, - "4xl": 32, + xs: typeScale.xs.fontSize, + sm: typeScale.sm.fontSize, + base: typeScale.base.fontSize, + lg: typeScale.md.fontSize, + xl: typeScale.lg.fontSize, + "2xl": typeScale.xl.fontSize, + "3xl": typeScale.xxl.fontSize, + "4xl": typeScale.xxl.fontSize, } as const; -// Per-size line heights — defined here, never inline +// Per-size line heights — mirrors the mapping above, never inline const sizeLineHeightMap = { - xs: 16, - sm: 18, - base: 20, - lg: 24, - xl: 26, - "2xl": 30, - "3xl": 38, - "4xl": 38, + xs: typeScale.xs.lineHeight, + sm: typeScale.sm.lineHeight, + base: typeScale.base.lineHeight, + lg: typeScale.md.lineHeight, + xl: typeScale.lg.lineHeight, + "2xl": typeScale.xl.lineHeight, + "3xl": typeScale.xxl.lineHeight, + "4xl": typeScale.xxl.lineHeight, } as const; // Comfortable leading for a raw numeric fontSize. Mirrors the ratios baked into @@ -278,8 +280,8 @@ export const TextRoot = forwardRef( calculatedFontSize = typeof size === "number" ? size : sizeMap[size]; } - // Use default if still undefined - calculatedFontSize = calculatedFontSize || 16; + // Use default if still undefined — the body size + calculatedFontSize = calculatedFontSize || sizeMap.base; // Calculate final styles const finalStyles: TextStyle = { diff --git a/js/components/src/components/ui/textarea.tsx b/js/components/src/components/ui/textarea.tsx index 5da18d9c9..3d9c2b39a 100644 --- a/js/components/src/components/ui/textarea.tsx +++ b/js/components/src/components/ui/textarea.tsx @@ -42,8 +42,7 @@ const Textarea = React.forwardRef( backgroundColor: th.theme.colors.input, color: th.theme.colors.text1, borderRadius: borderRadius.md, - // ≥16px so mobile web browsers don't zoom on focus - fontSize: typeScale.md.fontSize, + fontSize: typeScale.base.fontSize, fontFamily: fontFamilies.regular, }, props.editable === false && { opacity: 0.5 }, diff --git a/js/components/src/lib/theme/tokens.ts b/js/components/src/lib/theme/tokens.ts index 5eea9f33b..bc9d40d9c 100644 --- a/js/components/src/lib/theme/tokens.ts +++ b/js/components/src/lib/theme/tokens.ts @@ -555,7 +555,7 @@ export const borderRadius = { /** * Typography — one typeface (Geist), one modular scale: - * 12 / 13 / 14 / 16 / 20 / 24 / 32. Weights 400 / 500 / 600 only. + * 14 / 15 / 16 / 18 / 22 / 26 / 34. Weights 400 / 500 / 600 only. * Line heights live here, never inline. Sizes ≥20 get tight letter-spacing. * * `typeScale` is the canonical scale. The `ios` / `android` / `universal` objects @@ -564,46 +564,46 @@ export const borderRadius = { */ export const typeScale = { xs: { - fontSize: 12, - lineHeight: 16, + fontSize: 14, + lineHeight: 18, fontWeight: "400" as const, fontFamily: "Geist-Regular", }, sm: { - fontSize: 13, - lineHeight: 18, + fontSize: 15, + lineHeight: 20, fontWeight: "400" as const, fontFamily: "Geist-Regular", }, base: { - fontSize: 14, - lineHeight: 20, + fontSize: 16, + lineHeight: 22, fontWeight: "400" as const, fontFamily: "Geist-Regular", }, md: { - fontSize: 16, - lineHeight: 24, + fontSize: 18, + lineHeight: 26, fontWeight: "400" as const, fontFamily: "Geist-Regular", }, lg: { - fontSize: 20, - lineHeight: 26, + fontSize: 22, + lineHeight: 28, letterSpacing: -0.2, fontWeight: "500" as const, fontFamily: "Geist-Medium", }, xl: { - fontSize: 24, - lineHeight: 30, + fontSize: 26, + lineHeight: 32, letterSpacing: -0.3, fontWeight: "600" as const, fontFamily: "Geist-SemiBold", }, xxl: { - fontSize: 32, - lineHeight: 38, + fontSize: 34, + lineHeight: 40, letterSpacing: -0.5, fontWeight: "600" as const, fontFamily: "Geist-SemiBold", @@ -671,7 +671,7 @@ export const typography = { }, // Universal typography scale (keys kept for compatibility; values snap to - // the canonical 12/13/14/16/20/24/32 scale) + // the canonical 14/15/16/18/22/26/34 scale) universal: { xs: typeScale.xs, sm: typeScale.sm, @@ -688,44 +688,44 @@ export const typography = { // ingest URLs, diagnostics) mono: { xs: { - fontSize: 12, - lineHeight: 16, + fontSize: 14, + lineHeight: 18, fontWeight: "400" as const, fontFamily: "GeistMono-Regular", }, sm: { - fontSize: 13, - lineHeight: 18, + fontSize: 15, + lineHeight: 20, fontWeight: "400" as const, fontFamily: "GeistMono-Regular", }, base: { - fontSize: 14, - lineHeight: 20, + fontSize: 16, + lineHeight: 22, fontWeight: "400" as const, fontFamily: "GeistMono-Regular", }, lg: { - fontSize: 16, - lineHeight: 24, + fontSize: 18, + lineHeight: 26, fontWeight: "400" as const, fontFamily: "GeistMono-Regular", }, xl: { - fontSize: 20, - lineHeight: 26, + fontSize: 22, + lineHeight: 28, fontWeight: "500" as const, fontFamily: "GeistMono-Medium", }, "2xl": { - fontSize: 24, - lineHeight: 30, + fontSize: 26, + lineHeight: 32, fontWeight: "600" as const, fontFamily: "GeistMono-SemiBold", }, "3xl": { - fontSize: 32, - lineHeight: 38, + fontSize: 34, + lineHeight: 40, fontWeight: "600" as const, fontFamily: "GeistMono-SemiBold", },