diff --git a/AGENTS.md b/AGENTS.md index 4e03edf..0043680 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -135,7 +135,7 @@ Custom themes are CSS-first. Partial overrides inherit the remaining defaults. A - Surfaces: `--bits-surface`, `--bits-panel`, `--bits-raised`, `--bits-muted`. - Borders: `--bits-line`, `--bits-line-strong`. -- Foreground: `--bits-ink`, `--bits-text`, `--bits-text-soft`, `--bits-text-muted`, `--bits-text-faint`. +- Foreground: `--bits-ink`, `--bits-text`, `--bits-text-soft`, `--bits-text-dim`, `--bits-text-muted`, `--bits-text-faint`. - Brand: `--bits-primary`, `--bits-primary-soft`, `--bits-primary-shadow`. - Status: `--bits-amber`, `--bits-success`, `--bits-danger`, `--bits-danger-soft`, `--bits-danger-shadow`. - Effects: `--bits-shadow`, `--bits-backdrop`. @@ -188,11 +188,12 @@ Use block `Code` for preformatted machine text and inline `Code` for short ident ### Typography -Use `Heading` with levels 1–6 and `Text` with sizes `body`, `small`, `caption`, or `label`. Text tones are `default`, `soft`, `muted`, and `faint`. +Use `Heading` with levels 1–6 and `Text` with sizes `body`, `small`, `caption`, or `label`. Text tones are `default`, `soft`, `dim`, `muted`, and `faint`. - Choose heading levels by document structure, not desired visual size. - Use `label` for compact metadata and control labels, not paragraphs. -- Use `muted` and `faint` only for genuinely secondary content. +- Use `dim` for readable subordinate interface text such as inactive navigation. +- Use `muted` for metadata and placeholders, and `faint` only for nonessential decorative text. - Do not apply display typography to long body copy. ### Input and Textarea diff --git a/README.md b/README.md index 6ae729a..58f22af 100644 --- a/README.md +++ b/README.md @@ -178,6 +178,7 @@ The stylesheet bundles Latin JetBrains Mono and Silkscreen font files. CSS custo --bits-primary-shadow: #15576b; --bits-surface: #081015; --bits-panel: #0c1820; + --bits-text-dim: #9ab6c2; } ``` @@ -185,6 +186,8 @@ Built-in themes are `ember` (the warm, orange original) and `ocean` (blue/cyan). The public variables cover surfaces, borders, text, semantic colors, shadows and backdrops, typography, font sizes, spacing, border width, and focus rings. Every public variable is prefixed with `--bits-`. +Use `--bits-text-dim` for readable subordinate interface text, such as inactive navigation; reserve `--bits-text-muted` for supporting metadata and placeholders. + The theme's bare-element typography and link rules are low-specificity defaults built with `:where()`. A single application class can therefore override them without selector duplication or `!important`. diff --git a/docs/evidence/issue-11-text-ramp-ember.jpg b/docs/evidence/issue-11-text-ramp-ember.jpg new file mode 100644 index 0000000..a26e3bd Binary files /dev/null and b/docs/evidence/issue-11-text-ramp-ember.jpg differ diff --git a/docs/evidence/issue-11-text-ramp-ocean.jpg b/docs/evidence/issue-11-text-ramp-ocean.jpg new file mode 100644 index 0000000..c37288b Binary files /dev/null and b/docs/evidence/issue-11-text-ramp-ocean.jpg differ diff --git a/src/components/Typography.stories.tsx b/src/components/Typography.stories.tsx index 92e7a25..3b4a009 100644 --- a/src/components/Typography.stories.tsx +++ b/src/components/Typography.stories.tsx @@ -29,12 +29,25 @@ export const Scale: Story = { heading six · label Body text uses JetBrains Mono with a readable 1.65 line height. Small supporting copy. + Readable subordinate interface text. Caption and metadata copy. uppercase label ) }; +export const ToneRamp: Story = { + render: () => ( +
+ Default · primary content + Soft · secondary readable content + Dim · subordinate navigation and interface text + Muted · supporting metadata and placeholders + Faint · lowest-emphasis decorative text +
+ ), +}; + export const NativeElements: Story = { render: () => (
diff --git a/src/components/Typography.tsx b/src/components/Typography.tsx index aea540b..a4b261d 100644 --- a/src/components/Typography.tsx +++ b/src/components/Typography.tsx @@ -14,11 +14,12 @@ export function Heading({ level = 2, className, ...props }: HeadingProps) { export type TextSize = 'body' | 'small' | 'caption' | 'label'; export type TextElement = 'p' | 'span' | 'div' | 'label' | 'code'; +export type TextTone = 'default' | 'soft' | 'dim' | 'muted' | 'faint'; export interface TextProps extends ComponentPropsWithoutRef<'p'> { as?: TextElement; size?: TextSize; - tone?: 'default' | 'soft' | 'muted' | 'faint'; + tone?: TextTone; children?: ReactNode; } diff --git a/src/components/components.test.tsx b/src/components/components.test.tsx index 6ed2967..23ed180 100644 --- a/src/components/components.test.tsx +++ b/src/components/components.test.tsx @@ -165,6 +165,9 @@ describe('display components', () => { it('exposes only live Ember and Ocean theme tokens', () => { expect(builtInThemes).toEqual(['ember', 'ocean']); expect(colors.primary).toBe('var(--bits-primary)'); + expect(colors.textDim).toBe('var(--bits-text-dim)'); + expect(stylesheet).toContain('--bits-text-dim: #a9a394'); + expect(stylesheet).toContain('--bits-text-dim: #9ab6c2'); }); it('exposes semantic progress values', () => { @@ -190,9 +193,9 @@ describe('display components', () => { }); it('renders semantic typography', () => { - render(<>SectionSupporting copy); + render(<>SectionSubordinate navigation); expect(screen.getByRole('heading', { level: 3 })).toHaveClass('bits-heading--3'); - expect(screen.getByText('Supporting copy')).toHaveClass('bits-text--muted'); + expect(screen.getByText('Subordinate navigation')).toHaveClass('bits-text--dim'); }); it('keeps a semantic table inside a named keyboard-scrollable region', () => { diff --git a/src/stories/Theme.mdx b/src/stories/Theme.mdx index 74177ff..a2f4b6c 100644 --- a/src/stories/Theme.mdx +++ b/src/stories/Theme.mdx @@ -72,6 +72,7 @@ complete color theme, define every token in the color table below. --bits-text: #f4f4ff; --bits-text-soft: #cccced; + --bits-text-dim: #adadd2; --bits-text-muted: #9696bd; --bits-text-faint: #707095; @@ -121,6 +122,7 @@ a stable selector or application state value: --bits-danger-shadowPressed and dimensional danger shadow --bits-textPrimary text --bits-text-softSecondary readable text + --bits-text-dimReadable subordinate navigation and interface text --bits-text-mutedSupporting and placeholder text --bits-text-faintLowest-emphasis decorative text --bits-shadowGeneral component shadow color diff --git a/src/stories/story.css b/src/stories/story.css index 9a1276e..eb6595b 100644 --- a/src/stories/story.css +++ b/src/stories/story.css @@ -34,5 +34,6 @@ body { margin: 0; background: var(--bits-surface); color: var(--bits-text); } --bits-primary: #8f8fff; --bits-primary-soft: #c2c2ff; --bits-primary-shadow: #35357e; + --bits-text-dim: #adadd2; } @media (max-width: 600px) { .story-shell { padding: 16px; } } diff --git a/src/styles.css b/src/styles.css index 79c91d9..067882d 100644 --- a/src/styles.css +++ b/src/styles.css @@ -40,6 +40,7 @@ --bits-danger-soft: #ff8b72; --bits-text: #f2ede1; --bits-text-soft: #c9c3b4; + --bits-text-dim: #a9a394; --bits-text-muted: #8f8a7c; --bits-text-faint: #6f6a5e; --bits-shadow: rgba(0, 0, 0, .5); @@ -83,6 +84,7 @@ --bits-danger-soft: #ff9b9c; --bits-text: #edfaff; --bits-text-soft: #bad3dc; + --bits-text-dim: #9ab6c2; --bits-text-muted: #7fa2b0; --bits-text-faint: #587783; } @@ -178,6 +180,7 @@ .bits-text--label { font-size: var(--bits-font-size-label); line-height: 1.4; letter-spacing: .18em; text-transform: uppercase; } .bits-text--default { color: var(--bits-text); } .bits-text--soft { color: var(--bits-text-soft); } +.bits-text--dim { color: var(--bits-text-dim); } .bits-text--muted { color: var(--bits-text-muted); } .bits-text--faint { color: var(--bits-text-faint); } .bits-theme :where(small) { color: var(--bits-text-muted); font-size: var(--bits-font-size-caption); line-height: 1.5; } diff --git a/src/tokens.ts b/src/tokens.ts index 153dc28..ec52e2e 100644 --- a/src/tokens.ts +++ b/src/tokens.ts @@ -15,6 +15,7 @@ export const colors = { dangerSoft: 'var(--bits-danger-soft)', text: 'var(--bits-text)', textSoft: 'var(--bits-text-soft)', + textDim: 'var(--bits-text-dim)', textMuted: 'var(--bits-text-muted)', textFaint: 'var(--bits-text-faint)', } as const;