Tactile
A button as an object: light from above, a shadow in the tone's color below, a response with its body.
Specimen
| Role | Light | Dark |
|---|---|---|
--gr-bg | #f8fafc | #0f172a |
--gr-card | #ffffff | #1e293b |
--gr-fg | #0f172a | #f8fafc |
--gr-muted-fg | #556275 | #aab6c7 |
--gr-primary | #4f46e5 | #9d8bfa |
--gr-accent | #eef2ff | #1e1b4b |
What it is for
Product screens and landing pages — screens where the main action should invite a press. Volume, a lift
under the pointer and a spring in the response make the button an object rather than a label in a frame.
The theme is an heir of slate (extends): the palette, the roles and every component except the button
are Slate’s, and its edits reach Tactile on the next build. The only thing of its own is the button’s
language.
Rules of use
- Solid is the main action of the screen. The tone-colored shadow draws the eye; two solid buttons side by side
glow onto each other. The second action is
secondaryoroutline. - The main button of the
neutraltone is ink: the page’s text color, inverted in the dark mode. - Transparent weights respond with a backdrop growing from the center under the pointer, not with a fill
change: a row of
ghostbuttons reads as a row of objects, not links. - The radius grows with the step — 8 / 8 / 10 / 12px. A pill is the app’s handle
--gr-button-radius: 999px, not the theme. - A disabled button is hatching and a dashed border: the state reads even without color.
- With
prefers-reduced-motionthere is no lift, scale or spring — only the color change remains.
Modes
| Mode | Purpose |
|---|---|
light | the default mode: the tone shadow at 42 %, a line of light along the top edge |
dark | the tone shadow is denser — 62 %, the light is quieter, the bottom edge is darker |
Pairs on the root
The user's choice stores the mode name, and the root gets the pair of the theme and the mode. Nobody writes the pair by hand: the build's manifest provides it.
- Identifier
tactile- Extends
slate
| Mode | Scheme | Pair on the root |
|---|---|---|
light · default | light | data-theme="tactile-light" |
dark | dark | data-theme="tactile-dark" |
Roles
The value of each role in every mode, and the contrast where it means something.
Surface roles
| Token | Light | Dark | Contrast |
|---|---|---|---|
--gr-bg--gr-bg | #f8fafc | #0f172a | |
--gr-fg--gr-fg | #0f172a | #f8fafc | 17.06 / 17.06against --gr-bg |
--gr-card--gr-card | #ffffff | #1e293b | |
--gr-card-fg--gr-card-fg | #0f172a | #f8fafc | 17.85 / 13.98against --gr-card |
--gr-popover--gr-popover | #ffffff | #1e293b | |
--gr-popover-fg--gr-popover-fg | #0f172a | #f8fafc | 17.85 / 13.98against --gr-popover |
--gr-muted--gr-muted | #f1f5f9 | #334155 | |
--gr-muted-fg--gr-muted-fg | #556275 | #aab6c7 | 5.65 / 5.04against --gr-muted |
--gr-secondary--gr-secondary | #e2e8f0 | #334155 | |
--gr-secondary-fg--gr-secondary-fg | #1e293b | #f1f5f9 | 11.87 / 9.45against --gr-secondary |
--gr-overlay-bg--gr-overlay-bg | rgb(15 23 42 / 0.45) | rgb(2 6 23 / 0.65) | |
--gr-brd--gr-brd | #e2e8f0 | #334155 | |
--gr-brd-strong--gr-brd-strong | var(--gr-brd) | var(--gr-brd) | |
--gr-input--gr-input | #e2e8f0 | #334155 | |
--gr-input-hover--gr-input-hover | var(--gr-input) | var(--gr-input) | |
--gr-input-focus-brd--gr-input-focus-brd | var(--gr-input) | var(--gr-input) | |
--gr-input-readonly-bg--gr-input-readonly-bg | var(--gr-bg) | var(--gr-bg) | |
--gr-input-readonly-brd--gr-input-readonly-brd | var(--gr-input) | var(--gr-input) | |
--gr-control-brd--gr-control-brd | #64748b | #94a3b8 | |
--gr-control-brd-hover--gr-control-brd-hover | var(--gr-control-brd) | var(--gr-control-brd) | |
--gr-ring--gr-ring | #6366f1 | #818cf8 |
State roles
| Token | Light | Dark | Contrast |
|---|---|---|---|
--gr-disabled-bg--gr-disabled-bg | #f1f5f9 | #273548 | |
--gr-disabled-fg--gr-disabled-fg | #94a3b8 | #64748b | |
--gr-disabled-brd--gr-disabled-brd | #e2e8f0 | #334155 | |
--gr-invalid-brd--gr-invalid-brd | var(--gr-danger) | var(--gr-danger) | |
--gr-invalid-ring--gr-invalid-ring | var(--gr-danger) | var(--gr-danger) | |
--gr-invalid-text--gr-invalid-text | var(--gr-danger-text) | var(--gr-danger-text) | 7.94 / 9.41against --gr-bg |
Action roles
| Token | Light | Dark | Contrast |
|---|---|---|---|
--gr-primary--gr-primary | #4f46e5 | #9d8bfa | |
--gr-primary-fg--gr-primary-fg | #ffffff | #0f172a | 6.29 / 6.38against --gr-primary |
--gr-accent--gr-accent | #eef2ff | #1e1b4b | |
--gr-accent-fg--gr-accent-fg | #3730a3 | #c7d2fe | 8.88 / 10.72against --gr-accent |
--gr-selected--gr-selected | var(--gr-primary) | var(--gr-primary) | |
--gr-selected-fg--gr-selected-fg | var(--gr-primary-fg) | var(--gr-primary-fg) | 6.29 / 6.38against --gr-selected |
--gr-selected-hover--gr-selected-hover | var(--gr-selected) | var(--gr-selected) |
Status roles
| Token | Light | Dark | Contrast |
|---|---|---|---|
--gr-success--gr-success | #10b981 | #34d399 | |
--gr-success-light--gr-success-light | #d1fae5 | #194536 | |
--gr-success-fg--gr-success-fg | #0f172a | #0f172a | 7.04 / 9.29against --gr-success |
--gr-primary-text--gr-primary-text | #3730a3 | #c7d2fe | 9.49 / 11.97against --gr-bg |
--gr-success-text--gr-success-text | #065f46 | #6ee7b7 | 7.34 / 11.71against --gr-bg |
--gr-warning--gr-warning | #f97316 | #fb923c | |
--gr-warning-light--gr-warning-light | #ffedd5 | #672e1c | |
--gr-warning-fg--gr-warning-fg | #0f172a | #0f172a | 6.37 / 7.89against --gr-warning |
--gr-warning-text--gr-warning-text | #7c2d12 | #fdba74 | 8.96 / 10.59against --gr-bg |
--gr-danger--gr-danger | #dc2626 | #f87171 | |
--gr-danger-light--gr-danger-light | #fee2e2 | #6a2522 | |
--gr-danger-fg--gr-danger-fg | #ffffff | #0f172a | 4.83 / 6.45against --gr-danger |
--gr-danger-text--gr-danger-text | #991b1b | #fca5a5 | 7.94 / 9.41against --gr-bg |
--gr-info--gr-info | #2563eb | #60a5fa | |
--gr-info-light--gr-info-light | #dbeafe | #213771 | |
--gr-info-fg--gr-info-fg | #ffffff | #0f172a | 5.17 / 7.02against --gr-info |
--gr-info-text--gr-info-text | #1e40af | #bfdbfe | 8.34 / 12.56against --gr-bg |
--gr-slate--gr-slate | #475569 | #94a3b8 | |
--gr-slate-light--gr-slate-light | #e2e8f0 | #313b4a | |
--gr-slate-fg--gr-slate-fg | #ffffff | #0f172a | 7.58 / 6.96against --gr-slate |
--gr-slate-text--gr-slate-text | #334155 | #cbd5e1 | 9.90 / 12.02against --gr-bg |
--gr-azure--gr-azure | #0077b6 | #38bdf8 | |
--gr-azure-light--gr-azure-light | #e0f2fe | #1b425c | |
--gr-azure-fg--gr-azure-fg | #ffffff | #0f172a | 4.87 / 8.33against --gr-azure |
--gr-azure-text--gr-azure-text | #075985 | #bae6fd | 7.23 / 13.45against --gr-bg |
Solid fill roles
| Token | Light | Dark | Contrast |
|---|---|---|---|
--gr-primary-solid--gr-primary-solid | var(--gr-primary) | #595ce6 | |
--gr-primary-solid-hover--gr-primary-solid-hover | var(--gr-primary-hover) | #5053dd | |
--gr-primary-solid-active--gr-primary-solid-active | var(--gr-primary-active) | #474acf | |
--gr-primary-solid-fg--gr-primary-solid-fg | var(--gr-primary-fg) | var(--gr-slate-0) | 6.29 / 5.12against --gr-primary-solid |
--gr-success-solid--gr-success-solid | #047857 | #047857 | |
--gr-success-solid-hover--gr-success-solid-hover | #036c4d | #036c4d | |
--gr-success-solid-active--gr-success-solid-active | #025f44 | #025f44 | |
--gr-success-solid-fg--gr-success-solid-fg | var(--gr-slate-0) | var(--gr-slate-50) | 5.48 / 5.24against --gr-success-solid |
--gr-warning-solid--gr-warning-solid | #c2410c | #c2410c | |
--gr-warning-solid-hover--gr-warning-solid-hover | #b53b0a | #b53b0a | |
--gr-warning-solid-active--gr-warning-solid-active | #9f3307 | #9f3307 | |
--gr-warning-solid-fg--gr-warning-solid-fg | var(--gr-slate-0) | var(--gr-slate-50) | 5.18 / 4.95against --gr-warning-solid |
--gr-danger-solid--gr-danger-solid | var(--gr-danger) | #b91c1c | |
--gr-danger-solid-hover--gr-danger-solid-hover | var(--gr-danger-hover) | #a91919 | |
--gr-danger-solid-active--gr-danger-solid-active | var(--gr-danger-active) | #931515 | |
--gr-danger-solid-fg--gr-danger-solid-fg | var(--gr-danger-fg) | var(--gr-slate-50) | 4.83 / 6.18against --gr-danger-solid |
--gr-info-solid--gr-info-solid | var(--gr-info) | #1d4ed8 | |
--gr-info-solid-hover--gr-info-solid-hover | var(--gr-info-hover) | #1e40af | |
--gr-info-solid-active--gr-info-solid-active | var(--gr-info-active) | #1e3a8a | |
--gr-info-solid-fg--gr-info-solid-fg | var(--gr-info-fg) | var(--gr-slate-50) | 5.17 / 6.41against --gr-info-solid |
--gr-slate-solid--gr-slate-solid | var(--gr-slate) | var(--gr-slate-600) | |
--gr-slate-solid-hover--gr-slate-solid-hover | var(--gr-slate-hover) | var(--gr-slate-700) | |
--gr-slate-solid-active--gr-slate-solid-active | var(--gr-slate-active) | var(--gr-slate-800) | |
--gr-slate-solid-fg--gr-slate-solid-fg | var(--gr-slate-fg) | var(--gr-slate-50) | 7.58 / 7.24against --gr-slate-solid |
--gr-azure-solid--gr-azure-solid | #0369a1 | #0369a1 | |
--gr-azure-solid-hover--gr-azure-solid-hover | #075985 | #075985 | |
--gr-azure-solid-active--gr-azure-solid-active | #0c4a6e | #0c4a6e | |
--gr-azure-solid-fg--gr-azure-solid-fg | var(--gr-slate-0) | var(--gr-slate-50) | 5.93 / 5.67against --gr-azure-solid |
Data visualization roles
| Token | Light | Dark |
|---|---|---|
--gr-chart-1--gr-chart-1 | #4f46e5 | #6366f1 |
--gr-chart-2--gr-chart-2 | #059669 | #34d399 |
--gr-chart-3--gr-chart-3 | #ea580c | #fb923c |
--gr-chart-4--gr-chart-4 | #0284c7 | #38bdf8 |
--gr-chart-5--gr-chart-5 | #db2777 | #f472b6 |
Elevation
| Token | Light | Dark |
|---|---|---|
--gr-shadow-1--gr-shadow-1 | 0 1px 2px rgba(15, 23, 42, 0.08) | 0 1px 2px rgba(0, 0, 0, 0.45) |
--gr-shadow-2--gr-shadow-2 | 0 8px 24px rgba(15, 23, 42, 0.14) | 0 8px 24px rgba(0, 0, 0, 0.55) |
--gr-shadow-3--gr-shadow-3 | 0 16px 48px rgba(15, 23, 42, 0.20) | 0 16px 48px rgba(0, 0, 0, 0.65) |