Skip to content
edmi-ui

Rules

The design decisions behind Edmi UI. Do not "improve" them.

  1. Flat by default; depth is elevation. Every component renders flat: a solid fill or --card, a 1px border, the plain shadcn look. Depth comes only from elevation (a prop on the component, or an ElevationProvider scope). Four levels: -1 sunken, 0 flat, +1 raised, +2 floating. Depth follows role: actions rise, fields sink, overlays float. Read the Elevation guide. link buttons and Tabs line never take depth; the old raised boolean is gone.
  2. Bevel, not lips. Raised is an inner rim, a 1px top highlight and a dark hairline. Floating is the same bevel plus ONE soft drop. There are no hard 2px or 4px lips and no stacked drops. Gradient faces use background-origin: border-box and a transparent border.
  3. Sunken is a soft inset. A --sk-bg fill, a --sk-bd border and a soft inset shadow. Never a hard dark top edge. Focus on a sunken field swaps the edge for the ring. The only blur allowed is on that inset and on the single floating drop.
  4. Button depths. Raised (+1) is the face gradient, a 1px top highlight and the bevel. Floating (+2) adds a thicker gloss and a drop, for the one hero action per view. Filled buttons (default, destructive, brand) keep their colour when sunken (8% darker). White faces (the dark-mode primary, light secondary and outline) are a touch darker than white so the white top edge is visible.
  5. Pressed is translateY(1px) plus a pressed inset shadow. Flat pressed is a slightly darker fill.
  6. Only the active part rises. Tabs, segmented controls, toggle groups, pagination, calendar, switch and slider never bevel their track, list or grid. With elevation="raised" only the active tab, segment or page, the selected day, the checked box and the thumb rise. A default (non-outline) toggle shows depth only when ON. On a calendar or date picker the level goes on the shell (popover or card), never on the day grid.
  7. Floating chips over another surface (reactions on a bubble, badges over a card edge) are solid (--popover, or color-mix(brand 14%, popover) when active) with a 1px border. No transparency, no outer ring.
  8. Inset panel: the header sits on the shell (--muted, level 0, never rises); the body is a --card plate with its own full radius, inset 2px from the shell on the left, right and bottom; with a footer the body keeps a 0 bottom gap and the footer returns to the shell without a divider. Raised bevels the body plate; floating also drops the shell.
  9. Message rows: the avatar is top-aligned with the sender line, or with the first bubble line when there is no header. The footer sits under the bubble on the message side. Ghost bubbles keep their vertical padding.
  10. One height per group. Controls side by side share the control height (36px); option rows in one group (questionnaire choices, choice cards) share one height.
  11. Numbers are mono and right-aligned in tables. Up is --success-text (never brand, which follows the theme), down is --destructive-text.
  12. Headings on marketing pages use a soft ink (#3d3c38 light) at weight 400-500, not black or bold.
  13. Brand badges, alerts and toasts use the soft fill plus a tinted 30-40% border, never solid fills, except the primary Badge.
  14. Dark mode uses a clearer ladder. Stone dark surfaces step up visibly (background, card, popover, secondary, border, input), so depth reads without black blocks. See Theming.
  15. Levels are relative to the parent. A surface inside a raised or floating surface drops to flat and keeps its border (no bevel on bevel). Buttons and fields are exempt. An explicit elevation always wins.