Button styles and colours
Redraw every Button in a Theme scope by its style and colour with buttonStyles and buttonColors.
Usage
import { Theme } from '@epilot/spark-ui/theme'<Theme
buttonStyles={{ soft: 'outline', surface: { fill: 'none', border: 'thick' } }}
buttonColors={{ neutral: 'primary', primary: { light: '#0E7A3D' } }}
>
<App />
</Theme>Buttons keep styleVariant and color in code. The Theme decides how each
Button style (solid, soft, surface, outline, ghost) and each
Button colour (primary, neutral, danger) is drawn. A Theme without
buttonStyles and buttonColors draws every button exactly as before.
Remap and Restyle
Each entry is keyed by the value in code and is either:
- a Remap: another style or colour name. Every button with that value is drawn as the other one.
- a Restyle: how that value looks. A style takes
fillandborderlevels; a colour takes a design colour.
A button is resolved in two steps: first the Remap of its own value (what it is
drawn as), then the Restyle of the drawn value (how that looks). Remaps take
one hop only, so { soft: 'outline', outline: 'ghost' } draws a soft button as
an outline.
destructive reads as danger and accent as primary. A button with
neither styleVariant nor color counts as the scope's styleVariant (or
solid) and primary; a button with only a color counts as solid.
buttonStyles
type SparkButtonStyles = {
[S in SparkStyle]?: Exclude<SparkStyle, S> | SparkButtonStyleLevels
}
interface SparkButtonStyleLevels {
fill?: 'strong' | 'subtle' | 'none'
border?: 'none' | 'thin' | 'thick'
}Every named style is a pair of levels, so a Restyle with a style's own levels draws exactly that style. A level left out keeps the style's own.
| Style | fill | border |
|---|---|---|
solid | strong | none |
soft | subtle | none |
surface | subtle | thin |
outline | none | thin |
ghost | none | none |
thick is a 2px border. Hover, text contrast, high contrast and dark mode
follow from the levels and the button's colour.
The Theme writes the levels as --spark-button-<style>-fill and
--spark-button-<style>-border on its element (0 none, 1 subtle or thin,
2 strong or thick).
buttonColors
interface SparkButtonColors {
primary?: 'neutral' | 'danger' | SparkButtonDesignColor
neutral?: 'primary' | 'danger' | SparkButtonDesignColor
danger?: SparkButtonDesignColor
}
interface SparkButtonDesignColor {
light: string
dark?: string
}A design colour is a hex. Spark generates a full scale from it, the same way
as accentColor, with text on a solid fill kept at AA contrast. Without
dark, the dark scale is generated from light.
danger can take a design colour but cannot be remapped to another colour.
Nested Themes
A nested Theme overrides single entries and inherits the rest. Dialogs and popovers opened from a scope use its entries.
<Theme buttonStyles={{ soft: 'outline', ghost: 'surface' }}>
<Theme buttonStyles={{ soft: 'solid' }}>
{/* soft draws solid, ghost still draws surface */}
</Theme>
</Theme>Skipping the Remap
<Button fixedLook> keeps its own styleVariant and color whatever the
Remaps say, but still takes the Restyle of those values. Use it for a slot
whose look is set on purpose, such as a portal's call to action.
<Button styleVariant="soft" color="neutral" fixedLook>
Fixed
</Button>styleVariant="media" ignores both maps.