radio
. However, the styles for a <button>
are also often assigned to an <a href>
link, so in this case it makes sense to create a button
class. The button
class has all the defaults for every aspect of the button: background color, text color, border radius, border color, drop-shadow. Additional default styles for :hover
and :active
states are also defined.
-
dash. In the case of the button
class, the modifiers might be -cta
, -primary
, -ghost
, -link
, etc. A CTA button would be button -cta
see Appendix B for a comparison between P -M and BEM naming conventions.
While modifiers can be local to an element or component, they can also be global. For instance, the design system states that disabled elements are 60% opacity, interactivity is disabled, and the “no” cursor shows when hovered. This can be set globally, with a -disabled
modifier (for better coverage, the selector would actually be [disabled], .-disabeled {}
).
Another possibility for a global modifier would be -primary
. For elements such as buttons, checkboxes, radio buttons, and badges, they should all have the same attributes for their primary
variation: the background color of the button would be the same as the color for the checkboxes, radio buttons, and badges. And the text color of the button would be the same as the text on the badge, the check in the checkbox, or the border of the radio button.
By setting these properties globally, they all carry through to the elements as needed. And because they’re on the global scope, which has a very low specificity, then they are easy to override in the component’s CSS scope. More on scope and overrides in the CSS Cascading Layers section
studs-styles.css
is where cascading layers and order is being defined (see CSS Cascading Layers for more details). This file must be outside of the Sass compiler, as Sass interprets the @import
rules incorrectly, and changes the order of imports. This is a known bug with the Sass-dart compiler.base.scss
is where all the basic page styles are. Elements that aren’t components and aren’t considered “styled elements” are styled here. e.g. <p>
, <em>
, <strong>
, <abbr>
, etc.overrides.scss
is where devs can put any style overrides, extended styles, or addition styles.themes.scss
is where all color tokens are defined and used.--surface-backgroud
or --action-selected-foreground
In addition to colors, other display properties such as elevation (usually shown with varying levels of drop shadows), corner radius, border thickness, and spacing (used for padding and margins) are also defined.
!important
. CSS Layers helps return !important
to it’s original purpose.
The layer loading order is reset, utilities, fonts, themes, base, components, layout, overrides
<p>
,<em>
, <h1>
, <h2>
, <h3>
etc.@layer components.my_new_component{ }
.@layer overrides.class-name-to-modify{}
--action-background
, -secondary-foreground
, --focus-color
, and --disabled
are all examples of purpose-based tokens. See Theming Capabilities for more about this, including light and dark themes, as well as app-based overrides.
-secondary
, -outline
, -destructive
, -ghost
, -link
.
For secondary
there aren’t a lot of changes from default, so we just need to add this after the &:active
selector.
outline
is similar, but with a border.
link
button has a few extra styles.
As part of the metrics, there are some relative t-shirt sizing which can be applied to most elements.Currently, STUDS only has a light theme implemented. However, the framework is there to add a dark theme once it’s been fully designed. Right now, we are forcing a light theme even if a user has chosen dark theme in their OS or browser settings.note Why define both variables and utility classes? consistency. To make sure that mediumBecause they’re based on the font size of the element, they can be applied as a utility class. So-md
is the same no matter where it’s used, and that a-md
class is available when needed.<button class="button -secondary -md">
is all you need to make a slightly smaller button. That being said, utility classes are generally avoided, because they are describing what an element looks like, instead of it’s function or purpose. However, the t-shirt sizing is here as a fallback. In most cases, the sizes would be changed on the component’s style overrides instead of assigning the class. For instance, if this was a button in a table header and it should be slightly smaller than the default, then styles for the button might beEND OF NOT IMPLEMENTED
card
components will be elements on a css grid, and the header
, .content
, and footer
will be subgrid items for a clean layout.
<div class='card -employee -lead'>
.
Since and employee card has only a couple changes or additions to the base card
styles, using modifiers makes this easier to code. And then the -lead
styles are just a couple overrides that works with the -employee
and card
selectors. See AppendixB. Why not BEM?* for an example of what BEM-style naming would be for this card component.
A. Why "purpose" instead of "appearance"?
.button .button-cta .button-orange__rounded
, we would have had to go back to every place a button was used and update the class names on each of those buttons. But instead, we used .button -cta
, and the only place those styles needed to be updated was in a single component style sheet.By using this approach, it removes using “look and feel” in class names, and instead concentrates on what a thing actually is or does. Just using button -cta
is a lot cleaner and future-proof.Let the styles do what the styles do best… style things.B. Why not BEM?
C. Why not Tailwind?
grid-cols-auto-fit
classminmax(200, 1fr)
. If you want to change the minmax sizes for a different grid, you’ll need to create additional utility classes for each minmax size you might need.Whereas with vanilla CSS, it would look like this:<div class="gird -display>
. I’m calling this grid “display”, just as an example. Our styles would look like this:_themes
file, and the colors will automatically update on the grid elements without ever having to touch the CSS. If the corner radius changes to a squared corner, then all we need to do is update a single line in the CSS and every grid cell automatically updates.If the element with the grid
class also has a -display
modifier, then it will get the themed overrides (which also have light, dark, custom, and high contrast themed variations). Because we set up locally scoped custom properties, all we need to do is change those values on the local root, and every child of that element using those variables will get the updated values.To do just light and dark themes using Tailwind, you would need to make sure darkMode is enabled in your config file.If your tools don’t let you use the platform, that’s not a tool. It’s an obstacle. When new features come out in HTML and CSS, and you’re not allowed to use them because you’re waiting for the update to version 3… then you’re using the wrong tool. Find a different tool
- Miriam Suzanne, 2025
D. Accessibility
E. Resources and further reading