/* * Styles for the token-driven component library. * * Rules here encode design decisions Mantine's variables cannot express on their * own — chiefly the two that change *form* rather than color between themes: * the dashed danger treatment and status-by-brightness. See ui-design.md. * * Nothing in this file hardcodes a color; every value comes from a token. */ /* ------------------------------------------------------------------ Icons */ .wrolpi-icon-spin { animation: wrolpi-icon-spin 0.9s linear infinite; } @keyframes wrolpi-icon-spin { to { transform: rotate(360deg); } } @media (prefers-reduced-motion: reduce) { .wrolpi-icon-spin { animation: none; } } .wrolpi-icon-stack { position: relative; display: inline-flex; line-height: 0; } /* * The corner glyph sits on the main one, so it carries the surface color behind * it — without that the two sets of strokes cross and neither reads. * * Which surface that is depends on where the stack was placed, and only the call * site knows: the file browser puts one inside a filled blue button, where a disc * of the page background is a hole punched in the button. `--icon-stack-bg` * defaults to the page, and a call site anywhere else sets it. */ .wrolpi-icon-stack-corner { position: absolute; /* rem, so the badge keeps its corner as the glyph it sits on grows. */ right: -0.1875rem; bottom: -0.1875rem; display: flex; background: var(--icon-stack-bg, var(--bg)); border-radius: 50%; padding: 1px; } /* * A stack inside a button is the one case the call site should not have to describe, * because the surface changes underneath it: Mantine paints a filled button from * `--button-bg`, and the disc has to follow it into whatever state the button is in. * The file browser's New Folder button is disabled in WROL mode, so a disc pinned to the * enabled fill would be a blue chip on a grey button -- the original hole, inverted. * * Only `filled` gets the first rule: an outline or subtle button sets `--button-bg` to * `transparent`, and there the surface behind the button is still the right answer. * `:not([data-variant])` is that same case -- Mantine omits the attribute entirely when a * call site names a color but no variant, which is how every filled button here is * written, and keying on the attribute alone matched none of them. `--button-bg` itself * is absent when a button names neither, hence the fallback chain. */ .mantine-Button-root:not([data-variant]) .wrolpi-icon-stack-corner, .mantine-Button-root[data-variant="filled"] .wrolpi-icon-stack-corner { background: var(--button-bg, var(--icon-stack-bg, var(--bg))); } /* * There is deliberately no `:disabled` override here any more. There used to be one pinning * the disc to `--mantine-color-disabled`, because Mantine repainted a disabled button grey * and a disc left on `--blue` became a blue chip on a grey surface. Disabled buttons now * keep their own fill (see the disabled-button rule further down), so `--button-bg` is * correct in both states and the rule above covers them. */ /* ---------------------------------------------------------------- Buttons */ /* * Dashed borders mean "destructive or failed", and nothing else may use them. * In night mode a destructive button drops its fill entirely: 1.5px keeps the * dashes legible at low brightness, where a 1px hairline dash disappears. */ /* * In light, dark, and amber the button stays filled, which `color="red"` already * does. Night is the exception, and it cannot be expressed by overriding * Mantine's `--button-*` variables: Mantine writes those as inline styles on the * element, which no stylesheet rule can outrank. Setting the real properties * works instead — this selector (0,2,0 plus an element) outranks the Mantine * class rule that reads the variables. */ html[data-theme="night"] .wrolpi-button-danger { background: transparent; color: var(--danger); border: 1.5px dashed var(--danger); } html[data-theme="night"] .wrolpi-button-danger:hover:not(:disabled) { background: var(--panel); } /* The icon inside inherits currentColor, so it follows without another rule. */ /* * An icon button is the same height as a labelled one, so a group of them lines up. * * Mantine gives ActionIcon a size scale unrelated to Button's -- `sm` is 22px against a * button's 36px -- which left the Details/Open pair on every archive card ragged, and any * other toolbar that mixes the two. It already ships the aligned scale as * `--ai-size-input-*`, for an ActionIcon standing beside an Input; those five values are * identical to `--button-height-*`. Pointing one at the other fixes every size at once, * including ones no call site uses yet. * * The `html` prefix is load-bearing. Mantine declares this scale on ActionIcon's own * hashed class, which has the same specificity as `.mantine-ActionIcon-root` and would win * on source order. */ html .mantine-ActionIcon-root { --ai-size-xs: var(--ai-size-input-xs); --ai-size-sm: var(--ai-size-input-sm); --ai-size-md: var(--ai-size-input-md); --ai-size-lg: var(--ai-size-input-lg); --ai-size-xl: var(--ai-size-input-xl); } /* ----------------------------------------------------------- Theme picker */ .wrolpi-theme-picker { display: grid; grid-template-columns: repeat(auto-fit, minmax(14.375rem, 1fr)); gap: 0.625rem; } .wrolpi-theme-option { display: flex; flex-direction: column; gap: 0.25rem; align-items: flex-start; text-align: left; padding: 0.625rem 0.75rem; background: var(--panel); color: var(--text); border: 1px solid var(--border); font: inherit; cursor: pointer; } .wrolpi-theme-option:hover { background: var(--head); } /* The selected theme is marked by a heavier border as well as color, so the selection survives in monochrome themes. */ .wrolpi-theme-option-active { border: 2px solid var(--blue); padding: 0.5625rem 0.6875rem; } html[data-theme="night"] .wrolpi-theme-option-active, html[data-theme="amber"] .wrolpi-theme-option-active { border-color: var(--text); } .wrolpi-theme-option:focus-visible { outline: 2px solid var(--blue); outline-offset: 1px; } .wrolpi-theme-option-name { display: inline-flex; align-items: center; gap: 0.4375rem; font-weight: 600; font-size: 0.8125rem; } .wrolpi-theme-option-description { font-size: 0.75rem; color: var(--muted); line-height: 1.4; } /* Separated from the theme grid: it configures the chosen theme, it is not a choice. */ .wrolpi-media-filter-setting { margin-top: 0.875rem; padding-top: 0.75rem; border-top: 1px solid var(--border); } /* ------------------------------------------------------------ Pagination */ /* * The pager is centred here rather than by whoever renders it. Three call sites wrapped it * in `
` or `text-align: center` and it still sat hard against the left edge. * * Mantine builds a Pagination as a BLOCK root -- full width, so those wrappers had nothing * to centre -- holding a flex Group that carries the controls and lays them out at * `flex-start`. `justify-content` on the root therefore does nothing at all; it has to * reach the Group. * * Setting `--group-justify` on the root does not work either: Group defaults that prop and * writes the variable INLINE on its own element, where an inherited value cannot reach it. * The property itself is fair game, though -- Mantine only ever reads the variable from a * hashed class -- so this rule at (0,2,0) is what actually lands. * * The Group already wraps by default (`--group-wrap` falls back to `wrap`), which matters * once the row is centred: centred overflow spills off BOTH edges, and the first page would * be unreachable rather than merely off-screen to the right. */ .wrolpi-pagination .mantine-Group-root { justify-content: center; } .wrolpi-pagination button { background: var(--panel); color: var(--text); border: 1px solid var(--border); } .wrolpi-pagination button[data-active] { background: var(--blue); color: var(--btn-text); border-color: var(--blue); } /* Night and amber have no second hue to spend, so the current page is marked by a heavier border and the panel surface instead of a filled block of color. */ html[data-theme="night"] .wrolpi-pagination button[data-active], html[data-theme="amber"] .wrolpi-pagination button[data-active] { background: var(--panel); color: var(--text); border: 2px solid var(--text); } .wrolpi-pagination button:disabled { color: var(--muted); background: var(--bg); } /* ---------------------------------------------------------------- Tab bar */ .wrolpi-tabs { display: flex; align-items: flex-end; justify-content: space-between; gap: 0.75rem; border-bottom: 1px solid var(--border); /* * No `margin-bottom`. A tab bar is always the first block of a page, inside the page's own * stack, so the 0.875rem it used to carry was added to the stack's gap and put the bar 33px * from the content it labels where every other pair of blocks sat 17.6px apart. */ } .wrolpi-tabs-links { display: flex; flex-wrap: wrap; } .wrolpi-tabs-right { padding-bottom: 0.375rem; } .wrolpi-tab { display: inline-block; padding: 0.5rem 0.875rem; color: var(--muted); text-decoration: none; font-size: 0.8125rem; /* * A tab is a NavLink in the app's nav bars but a