Tactile

  • Themes
  • light
  • dark

A button as an object: light from above, a shadow in the tone's color below, a response with its body.

Values come from the @feugene/granularity-tokens package

On this page

Specimen

RoleLightDark
--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
The same markup in an island of each mode: the only difference is the pair on the subtree.

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 secondary or outline.
  • The main button of the neutral tone 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 ghost buttons 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-motion there is no lift, scale or spring — only the color change remains.

Modes

ModePurpose
lightthe default mode: the tone shadow at 42 %, a line of light along the top edge
darkthe 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
ModeSchemePair on the root
light · defaultlightdata-theme="tactile-light"
darkdarkdata-theme="tactile-dark"

Roles

The value of each role in every mode, and the contrast where it means something.

Surface roles

TokenLightDarkContrast
--gr-bg--gr-bg#f8fafc#0f172a
--gr-fg--gr-fg#0f172a#f8fafc17.06 / 17.06against --gr-bg
--gr-card--gr-card#ffffff#1e293b
--gr-card-fg--gr-card-fg#0f172a#f8fafc17.85 / 13.98against --gr-card
--gr-popover--gr-popover#ffffff#1e293b
--gr-popover-fg--gr-popover-fg#0f172a#f8fafc17.85 / 13.98against --gr-popover
--gr-muted--gr-muted#f1f5f9#334155
--gr-muted-fg--gr-muted-fg#556275#aab6c75.65 / 5.04against --gr-muted
--gr-secondary--gr-secondary#e2e8f0#334155
--gr-secondary-fg--gr-secondary-fg#1e293b#f1f5f911.87 / 9.45against --gr-secondary
--gr-overlay-bg--gr-overlay-bgrgb(15 23 42 / 0.45)rgb(2 6 23 / 0.65)
--gr-brd--gr-brd#e2e8f0#334155
--gr-brd-strong--gr-brd-strongvar(--gr-brd)var(--gr-brd)
--gr-input--gr-input#e2e8f0#334155
--gr-input-hover--gr-input-hovervar(--gr-input)var(--gr-input)
--gr-input-focus-brd--gr-input-focus-brdvar(--gr-input)var(--gr-input)
--gr-input-readonly-bg--gr-input-readonly-bgvar(--gr-bg)var(--gr-bg)
--gr-input-readonly-brd--gr-input-readonly-brdvar(--gr-input)var(--gr-input)
--gr-control-brd--gr-control-brd#64748b#94a3b8
--gr-control-brd-hover--gr-control-brd-hovervar(--gr-control-brd)var(--gr-control-brd)
--gr-ring--gr-ring#6366f1#818cf8

State roles

TokenLightDarkContrast
--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-brdvar(--gr-danger)var(--gr-danger)
--gr-invalid-ring--gr-invalid-ringvar(--gr-danger)var(--gr-danger)
--gr-invalid-text--gr-invalid-textvar(--gr-danger-text)var(--gr-danger-text)7.94 / 9.41against --gr-bg

Action roles

TokenLightDarkContrast
--gr-primary--gr-primary#4f46e5#9d8bfa
--gr-primary-fg--gr-primary-fg#ffffff#0f172a6.29 / 6.38against --gr-primary
--gr-accent--gr-accent#eef2ff#1e1b4b
--gr-accent-fg--gr-accent-fg#3730a3#c7d2fe8.88 / 10.72against --gr-accent
--gr-selected--gr-selectedvar(--gr-primary)var(--gr-primary)
--gr-selected-fg--gr-selected-fgvar(--gr-primary-fg)var(--gr-primary-fg)6.29 / 6.38against --gr-selected
--gr-selected-hover--gr-selected-hovervar(--gr-selected)var(--gr-selected)

Status roles

TokenLightDarkContrast
--gr-success--gr-success#10b981#34d399
--gr-success-light--gr-success-light#d1fae5#194536
--gr-success-fg--gr-success-fg#0f172a#0f172a7.04 / 9.29against --gr-success
--gr-primary-text--gr-primary-text#3730a3#c7d2fe9.49 / 11.97against --gr-bg
--gr-success-text--gr-success-text#065f46#6ee7b77.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#0f172a6.37 / 7.89against --gr-warning
--gr-warning-text--gr-warning-text#7c2d12#fdba748.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#0f172a4.83 / 6.45against --gr-danger
--gr-danger-text--gr-danger-text#991b1b#fca5a57.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#0f172a5.17 / 7.02against --gr-info
--gr-info-text--gr-info-text#1e40af#bfdbfe8.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#0f172a7.58 / 6.96against --gr-slate
--gr-slate-text--gr-slate-text#334155#cbd5e19.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#0f172a4.87 / 8.33against --gr-azure
--gr-azure-text--gr-azure-text#075985#bae6fd7.23 / 13.45against --gr-bg

Solid fill roles

TokenLightDarkContrast
--gr-primary-solid--gr-primary-solidvar(--gr-primary)#595ce6
--gr-primary-solid-hover--gr-primary-solid-hovervar(--gr-primary-hover)#5053dd
--gr-primary-solid-active--gr-primary-solid-activevar(--gr-primary-active)#474acf
--gr-primary-solid-fg--gr-primary-solid-fgvar(--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-fgvar(--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-fgvar(--gr-slate-0)var(--gr-slate-50)5.18 / 4.95against --gr-warning-solid
--gr-danger-solid--gr-danger-solidvar(--gr-danger)#b91c1c
--gr-danger-solid-hover--gr-danger-solid-hovervar(--gr-danger-hover)#a91919
--gr-danger-solid-active--gr-danger-solid-activevar(--gr-danger-active)#931515
--gr-danger-solid-fg--gr-danger-solid-fgvar(--gr-danger-fg)var(--gr-slate-50)4.83 / 6.18against --gr-danger-solid
--gr-info-solid--gr-info-solidvar(--gr-info)#1d4ed8
--gr-info-solid-hover--gr-info-solid-hovervar(--gr-info-hover)#1e40af
--gr-info-solid-active--gr-info-solid-activevar(--gr-info-active)#1e3a8a
--gr-info-solid-fg--gr-info-solid-fgvar(--gr-info-fg)var(--gr-slate-50)5.17 / 6.41against --gr-info-solid
--gr-slate-solid--gr-slate-solidvar(--gr-slate)var(--gr-slate-600)
--gr-slate-solid-hover--gr-slate-solid-hovervar(--gr-slate-hover)var(--gr-slate-700)
--gr-slate-solid-active--gr-slate-solid-activevar(--gr-slate-active)var(--gr-slate-800)
--gr-slate-solid-fg--gr-slate-solid-fgvar(--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-fgvar(--gr-slate-0)var(--gr-slate-50)5.93 / 5.67against --gr-azure-solid

Data visualization roles

TokenLightDark
--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

TokenLightDark
--gr-shadow-1--gr-shadow-10 1px 2px rgba(15, 23, 42, 0.08)0 1px 2px rgba(0, 0, 0, 0.45)
--gr-shadow-2--gr-shadow-20 8px 24px rgba(15, 23, 42, 0.14)0 8px 24px rgba(0, 0, 0, 0.55)
--gr-shadow-3--gr-shadow-30 16px 48px rgba(15, 23, 42, 0.20)0 16px 48px rgba(0, 0, 0, 0.65)