Skip to content

COMPONENT

Sheen

A band of light crossing a surface when you reach for it. One class on something that is already interactive, no layout change, and no colour to configure — it takes the ink already on the surface, so it inverts by itself.

IT DRAWS NOTHING UNTIL RELIEF IS TURNED UP

--ds-sheen defaults to --ds-relief, and the library ships that at zero — so on an untouched install this class is inert, and the specimen below will do nothing at all. That is deliberate rather than a coupling: a gleam on a surface that casts no shadow is a surface lit from a direction the rest of the page does not have.

Turn the dial up on the theme builder and come back, or read Relief for what else arrives with it.

Hover one — or Tab to it

HTML
<button type="button" class="btn sheen">SECONDARY</button>
<button type="button" class="btn btn-primary sheen">PRIMARY</button>
<button type="button" class="btn btn-on-cream sheen">ON CREAM</button>
<button type="button" class="btn sheen ripple" x-ds-ripple>SHEEN AND RIPPLE</button>

Reference

Read from components/sheen.css when this page was built, so it cannot fall behind the stylesheet.

CLASSES.sheen
SETTINGSSet the first on one element; a theme sets the second for all of them.--sheen-opacity — falls back to --ds-sheen-opacity--sheen-duration — falls back to --ds-sheen-duration
READS--ds-shine-ease--ds-shine-edge--ds-shine-tilt

Notes

IT IS THE RIPPLE'S BLADE ON A DIFFERENT TRIGGER

ripple-shine sweeps the same band across the same kind of surface. The difference is entirely when: a press is an answer — you did something and the surface acknowledges it — and this is an invitation. Same grammar, different sentence.

So the geometry is shared rather than copied. --ds-shine-edge, --ds-shine-tilt and --ds-shine-ease are read by both and declared by neither: what a blade of light looks like is one decision, and two spellings of it drift the first time somebody tunes one. Only the speed differs, and on purpose — an invitation that crosses at the speed of an answer reads as a twitch.

The two compose. This takes ::before and the ripple takes ::after, so a button can invite on hover and answer on release — the fourth one in the specimen does both.

TURNING IT ON
/* it is flat until the dial is up — see Relief */
:root {
  --ds-relief: 1;
}

/* or the one family, on its own */
:root {
  --ds-sheen: 1;
}
THE COLOUR IS NOT A DECISION

The band is currentcolor, and that is the whole design. A design system cannot ask every caller to say "light gleam here, dark gleam there" — that is a decision somebody gets wrong on the one button nobody tested, and the failure is invisible until it is looked at. A legible surface always has contrasting ink, so the ink is always the right highlight. Four fills in the specimen above, no configuration, and it inverts by itself.

TUNING ONE INSTANCE

Three unprefixed properties, the same two-level pattern flow uses for --flow and frame for --ratio: the system default is the token, and an instance overrides the local name.

/* One instance, slower and brighter than the system default. */
.hg-hero-cta {
  --sheen-duration: 1400ms;
  --sheen-opacity: 0.24;
}

/* A surface whose ink is the wrong highlight — the escape hatch. */
.hg-photo-tile {
  --sheen-ink: white;
}

DECLINED MOTION GETS NOTHING, AND THAT IS RIGHT

Everything that moves here is inside prefers-reduced-motion: no-preference, and there is no static fallback — unlike the ripple, which keeps a flat tint under a press. The difference is what each one owes. A control owes feedback to the person operating it; a gleam owes nobody anything. A reader who asked for less motion gets a surface with no highlight on it, which is a page rather than a defect.

TWO THINGS IT COSTS THE HOST

position: relative promotes the host to its own paint layer, which turns off subpixel antialiasing for its label — measured at 550 pixels over a 158×67 button, all of it edge softening and none of it legibility. And overflow: hidden clips a positioned child, so a surface with something deliberately hanging outside it should not wear this class. Both are the same prices ripple documents, for the same two lines of CSS, and both are paid only by an element that asked.