Buttons
.btn is one of the very few component primitives in Sulphuris. Most styling in Sulphuris is done with utilities; .btn exists because a button has enough stateful and interactive complexity (hover, motion, color inversion) that generating it from utilities alone would be verbose. Compose freely with utilities for anything the modifier classes don't cover.
Base: .btn
Resets <button> appearance, then applies:
| Property | Value (from $button) |
Emitted |
|---|---|---|
min-height |
56px |
3.5rem |
padding |
16px 32px |
1rem 2rem |
border-radius |
4px |
0.25rem |
background-color |
primary (#f6c026 light / #3F00FF dark) |
|
color |
foreground (#1a1a1d light / #ffffff dark) |
|
display |
inline-flex |
|
justify-content |
center |
|
align-items |
center |
Hover (pointer devices only โ applied inside $container-breakpoint / lg min-width media query):
background-color โ foreground (#1a1a1d light / #ffffff dark)
color โ background (#ffffff light / #1a1a1d dark)
The label is restated on hover because the two surfaces want opposite text.
primary carries no contrast floor โ it is a brand colour, free to be a yellow
โ so the only label guaranteed to be readable on it is foreground, while
foreground as a fill wants background. Both pairs clear WCAG AA: 10.3:1 and
17.4:1 light, 7.9:1 and 17.4:1 dark.
Motion โ color and background-color transitions are added when prefers-reduced-motion: no-preference is true. No transition is applied when the user has requested reduced motion.
Note
.btn also doubles as a reset when applied to a <button> element. The same reset rules (no border, no background, no padding, color: inherit) fire on both button.reset and .btn before the component styles kick in.
Usage
<!-- On an anchor -->
<a href="/start" class="btn">Get started</a>
<!-- On a native button -->
<button type="submit" class="btn">Submit</button>Modifier: .btn-inverted
Swaps fill to foreground with hover reverting to primary. The label swaps
with it, for the same reason .btn's does.
background-color: foreground โ hover: primary
color: background โ hover: foreground
<a href="#" class="btn btn-inverted">Inverted</a>Modifier: .btn-outline
Transparent background with a 2px solid border. The padding is reduced by the border width so the button stays the same overall height as .btn.
Exactly so at the default root. The padding converts under $rem-units and the border width does not โ border widths stay px everywhere, being fuzzy at fractional sizes โ so at a reader font-size other than 16px the two variants differ by under a pixel.
| Property | Value |
|---|---|
background |
transparent |
border |
2px solid foreground |
color |
foreground |
Hover fills with foreground and flips color to background.
<a href="#" class="btn btn-outline">Outline</a>Combined: .btn-outline.btn-inverted
Border and text use background color. Hover fills with background and sets color to foreground.
This one only works on a dark surface โ it paints itself in the page's own
background colour, so on the page background it is invisible rather than subtle.
Put it on foreground, on a photograph, or on any fill dark enough to carry it;
the sample gives it .bg-foreground for that reason.
<div class="bg-foreground p-16">
<a href="#" class="btn btn-outline btn-inverted">Outline inverted</a>
</div>Composing with utilities
.btn intentionally provides no typography, width, icon spacing, or shadow utilities โ use the Sulphuris utility set for those:
<!-- Rounded pill shape -->
<a href="#" class="btn rounded-32">Pill</a>
<!-- Full-width on mobile -->
<a href="#" class="btn d-block d-lg-inline-flex">Full width mobile</a>
<!-- Custom background via utility (overrides .btn fill) -->
<a href="#" class="btn bg-primary">Primary fill</a>Note
Because .btn sets background-color directly, a utility like .bg-primary will override it โ that is intentional. Sulphuris utilities avoid !important so the cascade is your control.
Retuning via $button
Override any key in the $button map before importing Sulphuris:
// _config-overrides.scss (loaded before @use "sulphuris")
@use "sulphuris/src/core/config" with (
$button: (
height: 48px,
padding-x: 24px,
padding-y: 12px,
border-width: 2px,
border-radius: 0
)
);
| Key | Default | Effect |
|---|---|---|
height |
56px |
min-height on .btn |
padding-x |
32px |
horizontal padding |
padding-y |
16px |
vertical padding |
border-width |
2px |
border on .btn-outline, inset padding compensation |
border-radius |
4px |
border-radius on .btn |
Written in px, emitted in rem under $rem-units โ border-width excepted, with every other border width. The label is already rem, so the box has to convert with it or a reader at a larger font-size gets scaled text in a fixed 56px frame.