um

CSS

Class names

BEM with a PascalCase block, every class prefixed, and state read from ARIA and data attributes.

Every class is BEM with a PascalCase block, and every block starts with Unmagic, so none collides with your app's classes or Tailwind's utilities.

Blocks, elements and modifiers

Block UnmagicTooltip
The component: the outermost element a helper renders.
Element UnmagicTooltip__popup
A part of it, after two underscores. Elements don't nest in the name: a part inside a part is still Block__part.
Modifier UnmagicButton--primary
A variant, after two dashes, worn alongside the block: class="UnmagicButton UnmagicButton--primary".

A modifier comes from a helper's options, such as variant:, size: or tone:, so you rarely write one by hand.

State comes from attributes

There are no state classes such as is-open or active. A component's state is styled from the attributes its markup already carries for accessibility or for its script:

.UnmagicTabs__tab[aria-selected="true"],
.UnmagicTabs__tab[aria-current="page"] {
  @apply bg-white text-neutral-900 dark:bg-neutral-700 dark:text-neutral-100;
}

.UnmagicClipboard[data-copied] .UnmagicClipboard__done {
  @apply block;
}

So the markup, what a screen reader hears and what's on screen can't disagree. For state of your own, set the attribute (aria-current="page", aria-invalid="true", disabled, hidden) and the style follows.

Custom elements

Behaviour lives in custom elements named unmagic-* (<unmagic-tooltip>, <unmagic-modal>). Most carry a block class as well. Those that only add behaviour, such as UnmagicOptimistic and UnmagicAutoscroll, are display: contents, so they don't take part in their parent's layout.

What's safe to target

Block and modifier names, and the ARIA and data- attributes a helper documents, are what the components' own Ruby and JavaScript rely on, so they stay stable. Element names and the markup inside a component can change between versions. To restyle part of a component, prefer the helper's options or a class: on it over a selector for its insides.

See it working