From 45593c2f06f29607dfce216b453a91308d10f8dc Mon Sep 17 00:00:00 2001 From: razeprasine Date: Wed, 29 Jul 2026 23:57:02 +0100 Subject: [PATCH 1/4] Add a design-system release checklist and change log --- .../PULL_REQUEST_TEMPLATE/design-system.md | 38 +++++++++++++++ packages/ui/CHANGELOG.md | 48 +++++++++++++++++++ packages/ui/CONTRIBUTING.md | 9 +++- 3 files changed, 93 insertions(+), 2 deletions(-) create mode 100644 .github/PULL_REQUEST_TEMPLATE/design-system.md create mode 100644 packages/ui/CHANGELOG.md diff --git a/.github/PULL_REQUEST_TEMPLATE/design-system.md b/.github/PULL_REQUEST_TEMPLATE/design-system.md new file mode 100644 index 0000000..315636f --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/design-system.md @@ -0,0 +1,38 @@ +## Design-system checklist + +Use this checklist when your PR changes design tokens, shared components in `packages/ui`, or introduces a new component to the design system. + +### API compatibility + +- [ ] Component API follows existing conventions: `className` forwarding, `cva` variants, `...props` spread last +- [ ] No breaking API changes without a **migration note** in the PR description (see changelog below) +- [ ] Subpath export added to `packages/ui/package.json` if this is a new component + +### Theme & visual + +- [ ] Verified in **light theme** and **dark theme** +- [ ] Verified at **mobile** viewport width (360px) +- [ ] No new raw hex colours, arbitrary font sizes, or arbitrary radius values — `bun run check:tokens` passes clean + +### Accessibility + +- [ ] Interactive elements are operable by **keyboard alone** (Tab, Enter/Escape, arrow keys where applicable) +- [ ] Correct ARIA roles and states are exposed (use Base UI primitives where possible) +- [ ] Colour is never the only signal — shape, label, or position accompanies any colour-coded state + +### Testing + +- [ ] Non-trivial logic includes a unit test (`*.test.tsx` alongside the component) +- [ ] Component added to the gallery (`apps/web/src/features/gallery/components/gallery-page.tsx`) +- [ ] Visual regression snapshots updated (`bun run test:e2e -- design-system-visual --update-snapshots`) and reviewed in the diff + +### Changelog + +- [ ] Entry added to [`packages/ui/CHANGELOG.md`](../../packages/ui/CHANGELOG.md) under the correct section (`Added`, `Changed`, `Deprecated`, `Removed`, `Fixed`, or `Accessibility`) +- [ ] Breaking changes include a `### Migration note` subsection + +### CI + +- [ ] `bun run typecheck --filter=@workspace/ui` passes +- [ ] `bun run lint --filter=@workspace/ui` passes +- [ ] `bun run test:e2e -- design-system-visual` passes (or snapshots intentionally updated) \ No newline at end of file diff --git a/packages/ui/CHANGELOG.md b/packages/ui/CHANGELOG.md new file mode 100644 index 0000000..bd08979 --- /dev/null +++ b/packages/ui/CHANGELOG.md @@ -0,0 +1,48 @@ +# Changelog + +All notable changes to `@workspace/ui` — the SO4 design system — are documented here. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Added + +### Changed + +### Deprecated + +### Removed + +### Fixed + +### Accessibility + +--- + +## [0.0.0] — 2026-07-29 — Baseline + +Initial release of the SO4 design system, extracted and standardized from the existing SO4 Markets application. + +### Added (Baseline) + +- **Surface tokens**: five explicit surface layers — `canvas`, `sunken`, `raised`, `overlay`, `interactive` — replacing the generic shadcn page/card/popover pattern. +- **Text tokens**: six text roles — `primary`, `secondary`, `tertiary`, `disabled`, `inverse`, `link`. +- **Trading-state colours**: foreground/subtle/border triplets for `long`, `short`, `liquidation`. +- **Semantic-status colours**: foreground/subtle/border triplets for `success`, `warning`, `info`, `danger`, `neutral`. Colour is never the only signal — shape, position, or label must accompany it. +- **Micro-typography scale**: 16 font-size tokens between 9.5px and 40px (`text-9-5` through `text-40`) for the app's dense data-table-heavy UI. +- **Radius scale**: `--radius-sm` through `--radius-4xl` derived from a single `--radius` root via `calc()`. +- **Components** (60+): `alert`, `app-shell`, `avatar`, `badge`, `breadcrumb`, `button`, `card`, `checkbox`, `data-table`, `dialog`, `dropdown-menu`, `empty-state`, `field`, `filter-chip`, `icon-button`, `input`, `keyboard-shortcut`, `loading-button`, `numeric`, `page-header`, `popover`, `progress-indicator`, `radio-group`, `scroll-area`, `select`, `separator`, `sheet`, `skeleton`, `slider`, `spinner`, `stat`, `states`, `status-badge`, `switch`, `table`, `table-toolbar`, `tabs`, `text`, `textarea`, `token-avatar`, `tooltip`, `transaction-status`. +- **Design-token check**: `scripts/check-design-tokens.ts` enforces token usage in CI — flags raw hex colours, arbitrary font sizes, and arbitrary radius classes. +- **Visual regression suite**: Playwright spec (`e2e/design-system-visual.spec.ts`) screenshots `/gallery` and all main routes at desktop and mobile widths in both themes. +- **Component gallery**: `/gallery` route rendering every component variant on one page for easy review. +- **Geist Mono**: available for monospaced contexts (order books, addresses, code). +- **Base UI primitives**: `@base-ui/react` as the accessibility foundation for all interactive components. + +### Deprecated + +- **shadcn aliases** (`--background`, `--card`, `--popover`): remain for backwards compatibility during migration. Prefer the surface-layer tokens (`--surface-*`) in new code. + +### Fixed + +- **DS-050 audit**: resolved 268 arbitrary-value violations (229 arbitrary font sizes, 38 raw hex colours, 1 arbitrary radius) across ~40 files — see [`DESIGN.md`](./DESIGN.md#audit-history) for details. \ No newline at end of file diff --git a/packages/ui/CONTRIBUTING.md b/packages/ui/CONTRIBUTING.md index 52b15f1..f2f8d36 100644 --- a/packages/ui/CONTRIBUTING.md +++ b/packages/ui/CONTRIBUTING.md @@ -1,6 +1,6 @@ # Contributing to `@workspace/ui` -`packages/ui` is the shared component library consumed by `apps/web` (and any future app in this monorepo) via the `@workspace/ui/*` import alias. This guide covers adding, testing, documenting, and exporting a component here — see the root [`CONTRIBUTING.md`](../../CONTRIBUTING.md) for the general repo workflow (branch naming, commit format, PR checklist). +`packages/ui` is the shared component library consumed by `apps/web` (and any future app in this monorepo) via the `@workspace/ui/*` import alias. This guide covers adding, testing, documenting, and exporting a component here — see the [design-system PR checklist](../../.github/PULL_REQUEST_TEMPLATE/design-system.md) for the pull-request requirements specific to token and shared-component changes. ## Does this belong in `packages/ui`, or in a feature? @@ -31,6 +31,7 @@ If you're unsure, default to the feature directory — it's a much smaller chang then add a `"test": "vitest run"` script to this package's `package.json` and a `component-name.test.tsx` alongside the component, following the render/assert patterns already used in `apps/web/src/**/*.test.tsx`. 5. **Export**: add both the component and its prop-types export to whichever consumer imports it via `@workspace/ui/components/` — no central barrel file exists (each component is its own subpath export per `package.json`'s `exports` map), so there's no index to update. 6. **Document in the gallery**: add your component to [`apps/web/src/features/gallery/components/gallery-page.tsx`](../../apps/web/src/features/gallery/components/gallery-page.tsx), rendering every variant/size it supports. This is how reviewers and future contributors discover what exists and see every state at a glance — see [`DESIGN.md`](../../DESIGN.md#the-component-gallery-gallery) for why it exists. +7. **Log in the changelog**: if the change is user-observable (new component, API change, deprecation, accessibility fix, token addition), add an entry to [`CHANGELOG.md`](./CHANGELOG.md) under the correct section — see the file's header for the expected sections. Breaking changes require a `### Migration note` subsection. ## Commands @@ -43,7 +44,11 @@ bun run check:tokens # design-token usage check (whole re bun run test:e2e -- design-system-visual # visual regression, once your component is in the gallery ``` -All four should be clean before opening a PR that touches this package — the same bar as the root [`CONTRIBUTING.md`](../../CONTRIBUTING.md) sets for the rest of the repo. +All four should be clean before opening a PR that touches this package. + +## Pull request + +Before opening or requesting review on a PR that touches `packages/ui` or the design tokens, run through the [design-system PR checklist](../../.github/PULL_REQUEST_TEMPLATE/design-system.md). It covers theme verification, keyboard accessibility, mobile layout, visual regression snapshots, API compatibility, the token check, and changelog entry requirements. ## Further reading From 387030d668df3a0d070f809e352980819a901e8f Mon Sep 17 00:00:00 2001 From: razeprasine Date: Thu, 30 Jul 2026 08:31:08 +0100 Subject: [PATCH 2/4] Resolve Audit layouts for RTL and logical CSS properties --- DESIGN.md | 43 +++++++ apps/web/src/app/providers/index.tsx | 5 +- .../earn/components/discover/discover-tab.tsx | 4 +- .../distributions/distributions-table.tsx | 6 +- .../earn/components/portfolio/assets-list.tsx | 2 +- .../earn/components/portfolio/rewards-bar.tsx | 2 +- .../gallery/components/gallery-page.tsx | 50 +++++--- .../features/pools/components/gm-pool-row.tsx | 4 +- .../pools/components/pool-actions.tsx | 2 +- .../distributions/distributions-tab.tsx | 6 +- .../components/traders/traders-tab.tsx | 2 +- .../features/trade/components/TradePage.tsx | 2 +- .../trade/components/chart/MarketSelector.tsx | 4 +- .../market-selector/MarketSelector.tsx | 4 +- .../positions/ClosePositionDialog.tsx | 4 +- .../components/positions/CollateralDialog.tsx | 4 +- .../positions/OrderExecutionFrozenBanner.tsx | 2 +- .../trade-panel/ConfirmationDialog.tsx | 2 +- .../components/trade-panel/TradePanel.tsx | 6 +- .../wallet/components/AccountBadge.tsx | 4 +- .../wallet/components/ConnectButton.tsx | 8 +- .../components/NetworkMismatchBanner.tsx | 2 +- .../web/src/shared/components/NumberInput.tsx | 4 +- .../web/src/shared/components/toast/toast.tsx | 117 ++++++++++++++++++ apps/web/src/ui/connect-button.tsx | 10 +- apps/web/src/ui/direction-provider.tsx | 38 ++++++ apps/web/src/ui/landing/hero.tsx | 12 +- apps/web/src/ui/landing/infrastructure.tsx | 6 +- apps/web/src/ui/landing/markets.tsx | 2 +- apps/web/src/ui/landing/stats.tsx | 4 +- apps/web/src/ui/landing/ticker.tsx | 2 +- e2e/design-system-visual.spec.ts | 33 ++++- packages/ui/src/components/alert.tsx | 2 +- packages/ui/src/components/badge.tsx | 4 +- packages/ui/src/components/button.tsx | 8 +- packages/ui/src/components/dialog.tsx | 2 +- packages/ui/src/components/dropdown-menu.tsx | 10 +- packages/ui/src/components/field.tsx | 2 +- packages/ui/src/components/popover.tsx | 2 +- packages/ui/src/components/scroll-area.tsx | 1 + packages/ui/src/components/select.tsx | 4 +- packages/ui/src/components/switch.tsx | 2 +- packages/ui/src/components/table-toolbar.tsx | 2 +- packages/ui/src/components/table.tsx | 2 +- packages/ui/src/components/tabs.tsx | 4 +- packages/ui/src/components/tooltip.tsx | 6 +- 46 files changed, 345 insertions(+), 102 deletions(-) create mode 100644 apps/web/src/shared/components/toast/toast.tsx create mode 100644 apps/web/src/ui/direction-provider.tsx diff --git a/DESIGN.md b/DESIGN.md index fb237bd..3765c14 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -120,9 +120,11 @@ then review the diffs in `e2e/design-system-visual.spec.ts-snapshots/` in your P - give reviewers one place to see every variant of a component at once instead of hunting through feature pages for one that happens to use the state you changed - act as the fixed, deterministic target for the visual regression suite above +- provide an **RTL toggle** (top-right corner) that switches the page between left-to-right and right-to-left layout, making directional-CSS regressions visible at a glance When you add a new component to `packages/ui`, add it to the gallery in the same PR — see [`packages/ui/CONTRIBUTING.md`](./packages/ui/CONTRIBUTING.md) for the full checklist. +<<<<<<< HEAD ## Separators and dividers Borders are the cheapest way to make a layout look organised and the fastest way to make it look noisy. Linear's calm hierarchy comes from *space* and *surface* doing the grouping, with lines reserved for the few places where a relationship genuinely needs marking. [`packages/ui/src/components/separator.tsx`](./packages/ui/src/components/separator.tsx) (DS-080) exists so those few places look the same everywhere. @@ -140,6 +142,47 @@ Borders are the cheapest way to make a layout look organised and the fastest way **Labelled dividers.** `` names a break instead of just drawing one — grouped form sections, "or" between auth methods, date breaks in a feed. The rules are decorative and the label is ordinary text, so assistive technology reads the words rather than the geometry. Label contrast comes from `text-text-secondary`, which holds up in light, dark, and high-contrast themes. Existing page borders were left alone in DS-080; migrate them opportunistically when you're already touching the markup. +======= +## Right-to-left (RTL) support + +### Logical CSS properties + +The design system uses **logical CSS properties** instead of physical directional properties wherever the behaviour is semantic (i.e. describes content flow rather than a fixed visual direction): + +| Physical | Logical equivalent | +|---|---| +| `left` / `right` | `inset-inline-start` / `inset-inline-end` | +| `margin-left` / `margin-right` | `margin-inline-start` / `margin-inline-end` | +| `padding-left` / `padding-right` | `padding-inline-start` / `padding-inline-end` | +| `border-left` / `border-right` | `border-inline-start` / `border-inline-end` | +| `text-left` / `text-right` | `text-start` / `text-end` | +| `translate-x` | Use `inset-inline-start` + `translate` or percent-based | + +Tailwind v4 provides logical utility classes — `ms-*`, `me-*`, `ps-*`, `pe-*`, `border-s-*`, `border-e-*`, `text-start`, `text-end`, `inset-inline-start-*`, `inset-inline-end-*` — which map to the correct physical side at runtime based on the `dir` attribute. + +### Justified physical directions + +Some directional properties are intentionally kept physical: + +- **Numeric financial values**: columns containing prices, sizes, volumes, APY, and other numbers use `text-right` (not `text-end`) because financial convention requires right-aligned digits in all locales, regardless of script direction. +- **Chart time flow**: time-series charts (candlesticks, line charts) render left-to-right regardless of page direction, per financial-market convention. +- **Data-attributed sides**: components that accept a physical `side` prop (e.g. `sheet` with `side="left"` or `side="right"`, tooltip arrow positioning for `data-[side=left]`/`data-[side=right]`) remain physical because they refer to the screen-relative position, not the content-flow direction. The logical `inline-start` / `inline-end` variants are also supported for direction-aware placement. + +### DirectionProvider + +A `DirectionProvider` (in `apps/web/src/ui/direction-provider.tsx`) persists the chosen direction to `localStorage` under `so4-direction` and sets the `dir` attribute on ``. It wraps the app in the provider tree alongside `ThemeProvider`. + +### RTL gallery fixture + +The component gallery at `/gallery` includes an RTL/LTR toggle button (top-right corner). Use it during development and review to verify that: + +- Dialogs, menus, fields, tabs, breadcrumbs, tables, and navigation remain usable in RTL +- Leading/trailing icons and controls mirror correctly +- Focus order follows DOM order and remains logical +- Numeric financial values preserve readable direction + +The visual regression suite (`e2e/design-system-visual.spec.ts`) captures gallery screenshots in both directions across all themes and viewports. Run `bun run test:e2e -- design-system-visual` to verify, or `bun run test:e2e -- design-system-visual --update-snapshots` to update baselines after an intentional change. +>>>>>>> e242e90 (Audit layouts for RTL and logical CSS properties) ## Audit history diff --git a/apps/web/src/app/providers/index.tsx b/apps/web/src/app/providers/index.tsx index 5d9e96c..0c98e73 100644 --- a/apps/web/src/app/providers/index.tsx +++ b/apps/web/src/app/providers/index.tsx @@ -4,6 +4,7 @@ import type { ErrorInfo, ReactNode } from "react" import { useWalletStore } from "@/features/wallet/store/wallet-store" import { NETWORK } from "@/app/config/network" import { ThemeProvider } from "@/ui/theme-provider" +import { DirectionProvider } from "@/ui/direction-provider" import { ErrorPage } from "@/app/error-page" import { validateIndexerConfig } from "@/app/config/indexer" import { useIndexerInvalidation } from "@/lib/graphql/use-indexer-invalidation" @@ -149,7 +150,9 @@ export function AppProviders({ children }: { children: ReactNode }) { - {children} + + {children} + diff --git a/apps/web/src/features/earn/components/discover/discover-tab.tsx b/apps/web/src/features/earn/components/discover/discover-tab.tsx index 3794da3..dbcf3f4 100644 --- a/apps/web/src/features/earn/components/discover/discover-tab.tsx +++ b/apps/web/src/features/earn/components/discover/discover-tab.tsx @@ -171,7 +171,7 @@ export function DiscoverTab() { -
+
Sort setSort("apy")}> APY {sort === "apy" && "↓"} @@ -247,7 +247,7 @@ export function DiscoverTab() { Short token
-

+

APY based on trailing 30-day performance

diff --git a/apps/web/src/features/earn/components/distributions/distributions-table.tsx b/apps/web/src/features/earn/components/distributions/distributions-table.tsx index 29869c0..2ab88e2 100644 --- a/apps/web/src/features/earn/components/distributions/distributions-table.tsx +++ b/apps/web/src/features/earn/components/distributions/distributions-table.tsx @@ -88,13 +88,13 @@ export function DistributionsTable({ distributions, onClaim }: DistributionsTabl
- + - + @@ -124,7 +124,7 @@ export function DistributionsTable({ distributions, onClaim }: DistributionsTabl )} - -
Epoch Date Amount Token StatusTxTx
+ {row.txHash ? ( {row.txHash.slice(0, 8)}… diff --git a/apps/web/src/features/earn/components/portfolio/assets-list.tsx b/apps/web/src/features/earn/components/portfolio/assets-list.tsx index e663631..22f7533 100644 --- a/apps/web/src/features/earn/components/portfolio/assets-list.tsx +++ b/apps/web/src/features/earn/components/portfolio/assets-list.tsx @@ -66,7 +66,7 @@ function LoadingRows() {
- +
diff --git a/apps/web/src/features/earn/components/portfolio/rewards-bar.tsx b/apps/web/src/features/earn/components/portfolio/rewards-bar.tsx index f6cd57f..245b422 100644 --- a/apps/web/src/features/earn/components/portfolio/rewards-bar.tsx +++ b/apps/web/src/features/earn/components/portfolio/rewards-bar.tsx @@ -149,7 +149,7 @@ export function RewardsBar() { isLoading={isLoading} /> -
+
>([40]) const [announceCount, setAnnounceCount] = useState(0) return (
-
-

Component Gallery

-

- Every packages/ui primitive, all variants. See{" "} - - DESIGN.md - {" "} - and{" "} - - packages/ui/CONTRIBUTING.md - {" "} - for how to add to this page. -

+
+
+

Component Gallery

+

+ Every packages/ui primitive, all variants. See{" "} + + DESIGN.md + {" "} + and{" "} + + packages/ui/CONTRIBUTING.md + {" "} + for how to add to this page. +

+
+
diff --git a/apps/web/src/features/pools/components/gm-pool-row.tsx b/apps/web/src/features/pools/components/gm-pool-row.tsx index 9d1e197..4f02842 100644 --- a/apps/web/src/features/pools/components/gm-pool-row.tsx +++ b/apps/web/src/features/pools/components/gm-pool-row.tsx @@ -46,7 +46,7 @@ function ValueCell({ className?: string }) { if (isLoading) { - return + return } return ( @@ -207,7 +207,7 @@ export function GmPoolRow({ market, variant, onMetricsChange }: GmPoolRowProps)
+ {pendingTx ? ( -
+

{pendingTx.mode === "deposit" ? "Deposit pending" : "Withdrawal pending"}

diff --git a/apps/web/src/features/referrals/components/distributions/distributions-tab.tsx b/apps/web/src/features/referrals/components/distributions/distributions-tab.tsx index 692e6a7..8e3e09f 100644 --- a/apps/web/src/features/referrals/components/distributions/distributions-tab.tsx +++ b/apps/web/src/features/referrals/components/distributions/distributions-tab.tsx @@ -97,13 +97,13 @@ export function DistributionsTab() {
- + - + @@ -118,7 +118,7 @@ export function DistributionsTab() { -
Epoch Date Amount Token USD valueActionAction
{formatToken(d.amount, d.token)} {d.token} {formatUsd(d.amountUsd)} +
) }