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-shadow | Pressed and dimensional danger shadow |
--bits-text | Primary text |
--bits-text-soft | Secondary readable text |
+ --bits-text-dim | Readable subordinate navigation and interface text |
--bits-text-muted | Supporting and placeholder text |
--bits-text-faint | Lowest-emphasis decorative text |
--bits-shadow | General 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;