UNPKG

raft-ui

Version:

React UI components for Raft.

371 lines (341 loc) • 13 kB
/* Steps pure CSS styling following pattern/Button.css architecture - Local CSS variables (--r-steps-*) declared on the list; steps inherit them - Each step maps its status to --r-step-* locals - Modifier classes rebind local variables - Component styles strictly consume local variables A step is its marker and text (`__main`) followed by the connector to the next step: across in a row, down in a column. */ /* ───────────────────────────────────────────────────────────────────────── 1. Local Variable Definitions & Modifier Overrides ───────────────────────────────────────────────────────────────────────── */ .r-steps { /* The Design System's marks: 20px squares with the number in Space Mono, joined by a short seam line. Upcoming is a seam outline on the surface, the current step a yellow sticker (ink edge, ink number), a finished step solid ink with a paper check. A failed step is a problem, so it is the orange sticker. One size: `sm` keeps the marks. */ --r-steps-indicator-size: 20px; --r-steps-indicator-font-size: 12px; --r-steps-icon-size: 14px; --r-steps-title-font-size: 13px; --r-steps-description-font-size: 12px; --r-steps-line-height: calc(18 / 13); --r-steps-gap: 8px; --r-steps-connector-gap: 8px; --r-steps-connector-min-width: 16px; --r-steps-vertical-item-gap: 16px; --r-steps-indicator-font-family: var(--font-mono); --r-steps-indicator-font-weight: 700; --r-steps-indicator-border-width: 1px; --r-steps-indicator-radius: var(--radius-sticker, 4px); --r-steps-title-font-family: var(--font-sans); --r-steps-title-font-weight: 600; --r-steps-connector-width: 1px; --r-steps-connector-color: var(--line-seam); --r-steps-connector-color-finish: var(--line-seam); --r-steps-focus-radius: var(--radius-sticker, 4px); --r-steps-focus-outline: 2px solid var(--line-strong); --r-steps-duration: 160ms; --r-steps-easing: ease-out; --r-steps-indicator-color-wait: var(--layer-card); --r-steps-indicator-color-process: var(--primary-400); --r-steps-indicator-color-finish: var(--foreground); --r-steps-indicator-color-error: var(--warning); --r-steps-indicator-border-color-wait: var(--line-seam); --r-steps-indicator-border-color-process: var(--sticker-ink); --r-steps-indicator-border-color-finish: var(--foreground); --r-steps-indicator-border-color-error: var(--sticker-ink); --r-steps-indicator-border-color-hover: var(--line-strong); --r-steps-indicator-text-color-wait: var(--foreground-hint); --r-steps-indicator-text-color-process: var(--sticker-ink); --r-steps-indicator-text-color-finish: var(--layer-canvas); --r-steps-indicator-text-color-error: var(--sticker-ink); --r-steps-indicator-shadow-process: none; --r-steps-indicator-shadow-hover: none; --r-steps-title-color-wait: var(--foreground-hint); --r-steps-title-color-process: var(--foreground); --r-steps-title-color-finish: var(--foreground-muted); --r-steps-title-color-error: var(--foreground); --r-steps-title-color-hover: var(--foreground); --r-steps-title-color-disabled: var(--foreground-disabled); } .r-step { /* `wait`: ahead of the current step. */ --r-step-indicator-color: var(--r-steps-indicator-color-wait); --r-step-indicator-border-color: var(--r-steps-indicator-border-color-wait); --r-step-indicator-text-color: var(--r-steps-indicator-text-color-wait); --r-step-indicator-shadow: none; --r-step-title-color: var(--r-steps-title-color-wait); --r-step-description-color: var(--foreground-hint); --r-step-connector-color: var(--r-steps-connector-color); &.r-step--process { --r-step-indicator-color: var(--r-steps-indicator-color-process); --r-step-indicator-border-color: var(--r-steps-indicator-border-color-process); --r-step-indicator-text-color: var(--r-steps-indicator-text-color-process); --r-step-indicator-shadow: var(--r-steps-indicator-shadow-process); --r-step-title-color: var(--r-steps-title-color-process); --r-step-description-color: var(--foreground-muted); } &.r-step--finish { --r-step-indicator-color: var(--r-steps-indicator-color-finish); --r-step-indicator-border-color: var(--r-steps-indicator-border-color-finish); --r-step-indicator-text-color: var(--r-steps-indicator-text-color-finish); --r-step-title-color: var(--r-steps-title-color-finish); --r-step-connector-color: var(--r-steps-connector-color-finish); } &.r-step--error { --r-step-indicator-color: var(--r-steps-indicator-color-error); --r-step-indicator-border-color: var(--r-steps-indicator-border-color-error); --r-step-indicator-text-color: var(--r-steps-indicator-text-color-error); --r-step-title-color: var(--r-steps-title-color-error); --r-step-description-color: var(--foreground-muted); } /* A clickable step under the pointer: the title takes the hover color, a waiting marker its hover border, and a settled marker the hover shadow. */ @media (hover: hover) { &.r-step--clickable:hover { --r-step-title-color: var(--r-steps-title-color-hover); } &.r-step--clickable.r-step--wait:hover { --r-step-indicator-border-color: var(--r-steps-indicator-border-color-hover); } &.r-step--clickable:not(.r-step--process):hover { --r-step-indicator-shadow: var(--r-steps-indicator-shadow-hover); } } &.r-step--disabled { --r-step-title-color: var(--r-steps-title-color-disabled); } } /* Stickers take the 1.5px ink edge; only the upcoming outline stays a 1px seam. */ .r-steps > .r-step:is(.r-step--process, .r-step--finish, .r-step--error) { --r-steps-indicator-border-width: 1.5px; } /* ───────────────────────────────────────────────────────────────────────── 2. Component Styles ───────────────────────────────────────────────────────────────────────── */ .r-steps { display: flex; line-height: var(--r-steps-line-height); list-style: none; margin: 0; padding: 0; } .r-step { display: flex; margin: 0; min-width: 0; padding: 0; position: relative; } /* The marker and the text; the hit area of a clickable step is drawn over it. */ .r-step__main { display: flex; gap: var(--r-steps-gap); position: relative; } .r-step__indicator { align-items: center; background: var(--r-step-indicator-color); border: var(--r-steps-indicator-border-width) solid var(--r-step-indicator-border-color); border-radius: var(--r-steps-indicator-radius); box-shadow: var(--r-step-indicator-shadow); box-sizing: border-box; color: var(--r-step-indicator-text-color); display: flex; flex: none; font-family: var(--r-steps-indicator-font-family); font-size: var(--r-steps-indicator-font-size); font-variant-numeric: tabular-nums; font-weight: var(--r-steps-indicator-font-weight); height: var(--r-steps-indicator-size); justify-content: center; line-height: 1; transition: background-color var(--r-steps-duration) var(--r-steps-easing), border-color var(--r-steps-duration) var(--r-steps-easing), box-shadow var(--r-steps-duration) var(--r-steps-easing), color var(--r-steps-duration) var(--r-steps-easing); width: var(--r-steps-indicator-size); & > svg { flex: none; height: var(--r-steps-icon-size); width: var(--r-steps-icon-size); } } /* The title's first line is centered on the marker. */ .r-step__content { min-width: 0; padding-top: calc( (var(--r-steps-indicator-size) - var(--r-steps-title-font-size) * var(--r-steps-line-height)) / 2 ); } .r-step__title { color: var(--r-step-title-color); display: block; font-family: var(--r-steps-title-font-family); font-size: var(--r-steps-title-font-size); font-weight: var(--r-steps-title-font-weight); transition: color var(--r-steps-duration) var(--r-steps-easing); white-space: nowrap; } .r-step__description { color: var(--r-step-description-color); font-size: var(--r-steps-description-font-size); margin-top: 2px; } /* Read out after the title: the marker's check or cross says it only by look. */ .r-step__status { clip-path: inset(50%); height: 1px; overflow: hidden; position: absolute; white-space: nowrap; width: 1px; } /* A clickable step's title is a button whose hit area (`::after`) stretches over the marker and the description; the focus ring is drawn around it. */ .r-step__button { appearance: none; background: none; border: 0; cursor: pointer; line-height: inherit; margin: 0; padding: 0; text-align: start; &::after { border-radius: var(--r-steps-focus-radius); content: ""; inset: -4px; position: absolute; } &:focus-visible { outline: none; } &:focus-visible::after { outline: var(--r-steps-focus-outline); } &:disabled { cursor: not-allowed; } } .r-step__connector { border-color: var(--r-step-connector-color); border-style: solid; border-width: 0; transition: border-color var(--r-steps-duration) var(--r-steps-easing); } .r-step:last-child > .r-step__connector { display: none; } /* A row, as the Design System's steps: the title sits beside its marker and the connector, a 16px seam line, follows the title on the same line into the next step; the description goes on the line below, under the title and the connector. The steps keep their own width and the row wraps. */ .r-steps--horizontal > .r-step { align-content: start; display: grid; flex: none; /* The connector's column holds its line and the gap on each side. */ grid-template-columns: var(--r-steps-indicator-size) auto calc(var(--r-steps-connector-min-width) + 2 * var(--r-steps-connector-gap)); &:last-child { grid-template-columns: var(--r-steps-indicator-size) auto; } & > .r-step__main, & > .r-step__main > .r-step__content { display: contents; } & > .r-step__main > .r-step__indicator { grid-column: 1; grid-row: 1; } & > .r-step__main > .r-step__content > .r-step__title { align-self: center; grid-column: 2; grid-row: 1; margin-inline-start: var(--r-steps-gap); } & > .r-step__main > .r-step__content > .r-step__description { grid-column: 2 / -1; grid-row: 2; margin-inline-start: var(--r-steps-gap); } & > .r-step__connector { align-self: center; border-top-width: var(--r-steps-connector-width); grid-column: 3; grid-row: 1; margin-inline: var(--r-steps-connector-gap); } } @media (width >= 40rem) { .r-steps--horizontal { flex-wrap: wrap; row-gap: 8px; } } /* Narrow screens: a row of titles beside their markers overflows a phone, so the steps share the width and each stacks its text under the marker, the titles wrapping. The marker and the connector share the top line; the text spans the step below. */ @media (width < 40rem) { .r-steps--horizontal > .r-step { --r-steps-connector-gap: 6px; --r-steps-connector-min-width: 8px; flex: 1 1 0; grid-template-columns: var(--r-steps-indicator-size) minmax(0, 1fr); &:last-child { grid-template-columns: var(--r-steps-indicator-size) minmax(0, 1fr); } & > .r-step__main > .r-step__content { display: block; grid-column: 1 / -1; grid-row: 2; padding-inline-end: var(--r-steps-connector-gap); padding-top: 6px; } & > .r-step__main > .r-step__content > .r-step__title, & > .r-step__main > .r-step__content > .r-step__description { margin-inline-start: 0; } & > .r-step__main > .r-step__content > .r-step__title { overflow-wrap: anywhere; white-space: normal; } & > .r-step__connector { grid-column: 2; } } } /* A column: the connector runs down from under each marker to just above the next. */ .r-steps--vertical { flex-direction: column; } .r-steps--vertical > .r-step { padding-bottom: var(--r-steps-vertical-item-gap); &:last-child { padding-bottom: 0; } & > .r-step__connector { border-inline-start-width: var(--r-steps-connector-width); bottom: 4px; inset-inline-start: calc((var(--r-steps-indicator-size) - var(--r-steps-connector-width)) / 2); position: absolute; top: calc(var(--r-steps-indicator-size) + 4px); } & > .r-step__main > .r-step__content > .r-step__title { white-space: normal; } } @media (prefers-reduced-motion: reduce) { .r-step__indicator, .r-step__title, .r-step__connector { transition: none; } }