Answer
Which token should I use for muted text?
Use --color-text-muted (#64748b)[1] for muted text on white. It measures 4.76:1 against --color-surface (#ffffff)[6], above the 4.5:1 that WCAG requires for normal text[5]. On the pale indigo --color-primary-surface it measures 4.26:1 and falls below that minimum[6], so use --color-text-secondary there.
Measured contrast of the six text roles
The six text roles are neutral slate tokens[1]. The table gives each one measured against white (--color-surface). The last column states whether the ratio reaches the 4.5:1 minimum for normal text[5]. The DS presents the first five as one neutral-hue text scale and --color-text-disabled as state only[2].
| Token | Hex | Ratio on white | Reaches 4.5:1 |
|---|---|---|---|
--color-text-heading | #0f172a[1] | 17.85:1[6] | Yes |
--color-text-strong | #1e293b[1] | 14.63:1[6] | Yes |
--color-text-body | #334155[1] | 10.35:1[6] | Yes |
--color-text-secondary | #475569[1] | 7.58:1[6] | Yes |
--color-text-muted | #64748b[1] | 4.76:1[6] | Yes |
--color-text-disabled | #cbd5e1[1] | 1.48:1[6] | No |
Where muted text stops working
Muted text clears the minimum on white and on the light gray surface, with a thin margin on the gray. It does not clear it on the indigo tint.[6][5]
The DS floor for muted text
Rule: muted text is never lighter than #64748b (slate 500) on white[3]. Why: lighter slate values fall below 4.5:1; slate 400 (#94a3b8) measures 2.56:1 on white[6], below the 4.5:1 minimum[5].
On --color-primary-surface use --color-text-secondary (#475569): it measures 6.78:1 there[6].
Copyable CSS
Import the tokens once at the app entry, then reference the role token. Never hardcode the hex[3].
@import '@ramoslabs/tokens/css';.card-meta {
color: var(--color-text-muted);
background: var(--color-surface);
}
.card-meta--on-indigo-tint {
color: var(--color-text-secondary);
background: var(--color-primary-surface);
}Reproduce the numbers
The ratios on this page come from the script below, which applies the WCAG relative-luminance and contrast-ratio definitions[4] to the published token values. Run it in a project that has @ramoslabs/tokens installed; it prints 4.76 and 6.29.
import { readFileSync } from 'node:fs'
// WCAG 2.2 relative luminance and contrast ratio, applied to the published token values.
const lin = (c) => ((c /= 255) <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4)
const lum = (hex) => {
const n = parseInt(hex.slice(1), 16)
return 0.2126 * lin((n >> 16) & 255) + 0.7152 * lin((n >> 8) & 255) + 0.0722 * lin(n & 255)
}
const ratio = (a, b) => {
const [hi, lo] = [lum(a), lum(b)].sort((x, y) => y - x)
return (hi + 0.05) / (lo + 0.05)
}
const tokens = JSON.parse(readFileSync('node_modules/@ramoslabs/tokens/dist/tokens.json', 'utf8'))
const pair = (fg, bg) => ratio(tokens[fg], tokens[bg]).toFixed(2)
console.log(pair('color-text-muted', 'color-surface')) // 4.76
console.log(pair('color-primary', 'color-surface')) // 6.29Frequently asked questions
- Which token should I use for muted text on a white background?
- Use
--color-text-muted(#64748b)[1]. It measures 4.76:1 on white[6], above the 4.5:1 minimum for normal text[5]. - Can I use a lighter gray for muted text?
- Not on white under the DS rule: muted text is never lighter than #64748b[3]. Slate 400 (#94a3b8) measures 2.56:1 on white[6], below 4.5:1[5].
- What should I use for muted text on an indigo tinted background?
- Use
--color-text-secondary(#475569).--color-text-mutedmeasures 4.26:1 on--color-primary-surface(#eef2ff), below 4.5:1, while secondary text measures 6.78:1 there[6]. - Is --color-text-disabled a good choice for less important text?
- No. It measures 1.48:1 on white[6] and the DS reserves it for the disabled state, not readable text[2].
- Does muted text pass on the light gray surface?
- Yes, with a thin margin: 4.55:1 on
--color-surface-secondary(#f8fafc)[6], against a 4.5:1 minimum[5]. - Should I hardcode the muted gray value?
- No. Reference
var(--color-text-muted)after importing@ramoslabs/tokens/cssonce; a raw hex in CSS is a defect under the DS rules[3].
References
Every factual statement above points to one of these. External sources are listed with their own date or version and the day we read them; DS sources are pinned to a repository commit; measurements say what was run and when.
- Source tokens: color.primitive.json and color.semantic.json (@ramoslabs/tokens)
- Colors: the foundation page (roles and measured contrast pairings; source Colors.md)
- AGENTS.md: accessibility floors, the hard rule, and component status
- WCAG 2.2: definitions of relative luminance and contrast ratio
- Understanding SC 1.4.3: Contrast (Minimum)
- Contrast ratios computed from the token hex values with the script on this page (WCAG relative-luminance and contrast-ratio formulas), rounded to two decimals
Machine-readable: llms-full.txt, tokens.json,MCP server. © 2026 RAMOS SOLUTIONS S.A.S. MIT License.