Skip to main content

Styling Guide

Monochrome is headless — no CSS is shipped. You provide all styles and control the visual appearance completely.

Philosophy

The library handles:
  • ARIA attribute management
  • Keyboard navigation
  • Focus management
  • State coordination
You handle:
  • Visual design
  • Layout
  • Animations
  • Theming

Required CSS for Menus

Menu components require specific CSS to function correctly. The core sets CSS custom properties via getBoundingClientRect() that you must use for positioning.

Popover Visibility

/* Make popovers visible when open */
[popover]:popover-open {
  display: flex;
}
The core sets --bottom, --left, --top, and --right custom properties on menu popovers:
/* Root-level menu positioning */
[role="menu"] {
  position: fixed;
  inset: auto;
  margin: 0;
  top: var(--bottom);
  left: var(--left);
}
/* Nested submenu positioning */
[role="menu"] [role="menu"] {
  top: var(--top);
  left: var(--right);
  margin-left: 8px;  /* gap so core detects opening direction */
}
The margin-left gap is critical. The core uses this spacing to detect whether a submenu should open to the right or left.

Safety Triangle (Submenu Hover Intent)

When a submenu is open, Monochrome creates a triangular safe zone so users can move diagonally to the submenu without accidentally closing it. The core sets a [data-safe] attribute on the Group element along with CSS custom properties:
  • --left — triangle point X coordinate
  • --center — triangle point Y coordinate (cursor Y)
  • --right — submenu edge X coordinate
  • --top — submenu top edge
  • --bottom — submenu bottom edge

Example Safety Triangle CSS

[data-safe] {
  clip-path: polygon(
    var(--left) var(--center),
    var(--right) var(--top),
    var(--right) var(--bottom)
  );
}
The safety triangle updates on every pointermove event, following the cursor to maintain a smooth diagonal path to the submenu.

Styling Component States

All component state is reflected in ARIA attributes. Style based on these attributes:

Accordion

/* Expanded state */
[aria-expanded="true"] {
  /* trigger styles when open */
}

/* Disabled state */
[aria-disabled="true"] {
  opacity: 0.5;
  cursor: not-allowed;
}

Tabs

/* Selected tab */
[role="tab"][aria-selected="true"] {
  border-bottom: 2px solid currentColor;
}

/* Hidden panel */
[role="tabpanel"][aria-hidden="true"] {
  /* Already has hidden="until-found" */
}
/* Checkbox item checked state */
[role="menuitemcheckbox"][aria-checked="true"]::before {
  content: '✓';
}

/* Radio item selected state */
[role="menuitemradio"][aria-checked="true"] {
  font-weight: bold;
}

/* Disabled menu items */
[role^="menuitem"][aria-disabled="true"] {
  opacity: 0.5;
}

Hidden Content

Monochrome uses hidden="until-found" instead of display: none to preserve browser find-in-page (Cmd+F / Ctrl+F).
/* Hidden panels are already hidden by the attribute */
/* Add transitions if desired */
[role="region"],
[role="tabpanel"] {
  transition: opacity 200ms;
}

[role="region"][aria-hidden="true"],
[role="tabpanel"][aria-hidden="true"] {
  opacity: 0;
}
When users search with Cmd+F and the browser finds hidden content, a beforematch event fires. Monochrome’s core listens for this and automatically opens the containing component.

Focus Styles

Monochrome manages focus automatically. Provide visible focus indicators:
/* Focus-visible for keyboard users */
button:focus-visible {
  outline: 2px solid blue;
  outline-offset: 2px;
}

/* Remove focus ring for mouse users */
button:focus:not(:focus-visible) {
  outline: none;
}

Animations

Add animations by observing ARIA attribute changes:
@keyframes slideDown {
  from {
    opacity: 0;
    transform: translateY(-8px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

[role="region"]:not([aria-hidden="true"]) {
  animation: slideDown 200ms ease-out;
}

Responsive Styling

All components work on mobile. Consider touch-friendly sizes:
/* Larger touch targets on mobile */
@media (pointer: coarse) {
  button {
    min-height: 44px;
    min-width: 44px;
  }
}

Best Practices

  1. Never use display: none — it breaks find-in-page. Monochrome uses hidden="until-found" by default.
  2. Style ARIA attributesaria-expanded, aria-selected, aria-checked reflect component state.
  3. Maintain spacing — The 8px margin-left on submenus is required for direction detection.
  4. Provide focus indicators — Keyboard users need visible focus states.
  5. Test with keyboard — Navigate with Arrow keys, Home, End, Enter, Space, Escape.
  6. Support high contrast — Use currentColor and semantic colors that adapt to system themes.

Build docs developers (and LLMs) love