Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,16 @@ jobs:
run: pnpm knip
- name: Check TypeScript regressions (Betterer)
run: pnpm exec betterer ci
# The workspace packages are formatted and linted by oxfmt/oxlint, not Biome, so they need
# their own steps. Each runs the package's own script, which keeps CI and local in step.
- name: "Check formatting (oxfmt): packages/ui"
run: pnpm --filter @infrahub/ui run format:check
- name: "Check linting (oxlint): packages/ui"
run: pnpm --filter @infrahub/ui run lint
- name: "Check formatting (oxfmt): packages/graph"
run: pnpm --filter @infrahub/graph run format:check
- name: "Check linting (oxlint): packages/graph"
run: pnpm --filter @infrahub/graph run lint

frontend-validate-openapi-types:
if: |
Expand Down
1 change: 1 addition & 0 deletions changelog/+frontend-packages-lint-ci.housekeeping.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The frontend design-system packages (`@infrahub/ui` and `@infrahub/graph`) are now format- and lint-checked in CI with their own toolchain (oxfmt and oxlint), alongside the app's existing Biome check. Both packages shipped that toolchain but nothing ever ran it, so a misformatted or lint-breaking file in either package could ship green.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: This PR closes #10390, so the fragment should carry the issue ID in its filename. Rename changelog/+frontend-packages-lint-ci.housekeeping.md to changelog/10390-frontend-packages-lint-ci.housekeeping.md; the + orphan prefix is only for fragments with no linked issue.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At changelog/+frontend-packages-lint-ci.housekeeping.md, line 1:

<comment>This PR closes #10390, so the fragment should carry the issue ID in its filename. Rename `changelog/+frontend-packages-lint-ci.housekeeping.md` to `changelog/10390-frontend-packages-lint-ci.housekeeping.md`; the `+` orphan prefix is only for fragments with no linked issue.</comment>

<file context>
@@ -0,0 +1 @@
+The frontend design-system packages (`@infrahub/ui` and `@infrahub/graph`) are now format- and lint-checked in CI with their own toolchain (oxfmt and oxlint), alongside the app's existing Biome check. Both packages shipped that toolchain but nothing ever ran it, so a misformatted or lint-breaking file in either package could ship green.
</file context>

18 changes: 18 additions & 0 deletions dev/knowledge/frontend/design-system.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,24 @@ Source of truth: `frontend/packages/graph/src/index.ts`. Adopted by `path-traver

A third directory exists under `frontend/packages/`: `plugins/` holds a single standalone Vite + module-federation plugin template (`plugins/template`) that is **not** a pnpm workspace member (the workspace lists `app`, `packages/schema-visualizer`, `packages/ui`, and `packages/graph`) and is unrelated to the design system.

## Formatting and linting: two toolchains, split by directory

`frontend/app` uses Biome. Both workspace packages use oxfmt and oxlint instead, each with its own
`oxfmt.config.ts` and `oxlint.config.ts` (`@infrahub/ui` also sorts Tailwind classes through oxfmt).
There is no Biome config anywhere under `frontend/packages/`, so **never run Biome there**: with no
`biome.jsonc` to find it walks up out of the checkout, picks up an unrelated config, and reformats
the whole file.

```bash
pnpm --filter @infrahub/ui run format # oxfmt --write
pnpm --filter @infrahub/ui run format:check # what CI runs
pnpm --filter @infrahub/ui run lint # oxlint
pnpm --filter @infrahub/ui run lint:fix # oxlint --fix
```

Same four scripts for `@infrahub/graph`. CI's `frontend-lint` job runs `format:check` and `lint` for
both packages next to the app's Biome check, so a misformatted package file fails the build.

## When to consume from `@infrahub/ui`

Always, for the components above. Do not reimplement them inline in feature code, even if it "feels lighter":
Expand Down
11 changes: 10 additions & 1 deletion frontend/app/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ cd frontend/app && pnpm codegen # Generate GraphQL types

## Before pushing (run the full CI gate locally)

`pnpm biome:fix` alone is **not** the CI gate. The `frontend-lint` job runs three checks and
`pnpm biome:fix` alone is **not** the CI gate. The `frontend-lint` job runs several checks and
`frontend-tests` runs the browser test suite. Run all of them before pushing — they each fail CI
independently:

Expand All @@ -31,6 +31,15 @@ cd frontend/app && pnpm exec betterer ci # TypeScript-regression gate (NOT plai
cd frontend/app && pnpm test # vitest (browser mode)
```

Biome covers `frontend/app` only. `frontend/packages/ui` and `frontend/packages/graph` are formatted
and linted by oxfmt/oxlint, and the same `frontend-lint` job gates them — never run Biome against
them. See `dev/knowledge/frontend/design-system.md` for why and for the per-package commands:

```bash
cd frontend/app && pnpm --filter @infrahub/ui run format:check && pnpm --filter @infrahub/ui run lint
cd frontend/app && pnpm --filter @infrahub/graph run format:check && pnpm --filter @infrahub/graph run lint
```

## See Also

### Guidelines (How to write code)
Expand Down
48 changes: 10 additions & 38 deletions frontend/packages/ui/src/styles/theme.css
Original file line number Diff line number Diff line change
Expand Up @@ -53,20 +53,12 @@
--selected-shadow: 0 0 #0000;
--selected-highlight: var(--highlight), var(--selected);

--card: linear-gradient(
to bottom,
var(--color-stone-50) 0%,
var(--color-white) 10%
);
--card: linear-gradient(to bottom, var(--color-stone-50) 0%, var(--color-white) 10%);
--card-shadow:
inset 0 2px 0 rgb(255 255 255), inset 0 -1px 2px 1px rgb(0 0 0 / 0.03),
0 1px 1px rgb(0 0 0 / 0.02);

--card-header: linear-gradient(
to bottom,
var(--color-white) 0%,
var(--color-neutral-50) 100%
);
--card-header: linear-gradient(to bottom, var(--color-white) 0%, var(--color-neutral-50) 100%);
--card-header-foreground: var(--color-neutral-700);
--card-header-shadow: inset 0 1px 0 rgb(255 255 255 / 0.85);

Expand All @@ -78,8 +70,7 @@

--modal-frame: --alpha(var(--color-white) / 25%);
--modal-frame-border: var(--border);
--modal-shadow:
0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
--modal-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);

--table-cell: var(--color-stone-50);
--table-cell-pinned: var(--color-white);
Expand Down Expand Up @@ -121,44 +112,25 @@
--popover: --alpha(var(--color-stone-900) / 70%);

--highlight:
radial-gradient(
72% 100% at 50% 100%,
rgb(255 255 255 / 0.09),
transparent 72%
),
radial-gradient(72% 100% at 50% 100%, rgb(255 255 255 / 0.09), transparent 72%),
linear-gradient(rgb(255 255 255 / 0.02), rgb(255 255 255 / 0.02));
--highlight-foreground: var(--foreground);

--selected:
radial-gradient(
72% 100% at 50% 100%,
rgb(255 255 255 / 0.16),
transparent 72%
),
radial-gradient(72% 100% at 50% 100%, rgb(255 255 255 / 0.16), transparent 72%),
linear-gradient(rgb(255 255 255 / 0.05), rgb(255 255 255 / 0.05));
--selected-foreground: var(--foreground);
--selected-shadow:
inset 0 1px 0 rgb(255 255 255 / 0.08), 0 1px 2px rgb(0 0 0 / 0.6);
--selected-shadow: inset 0 1px 0 rgb(255 255 255 / 0.08), 0 1px 2px rgb(0 0 0 / 0.6);

--card: linear-gradient(180deg, #201d1c 0%, #191716 55%, #141312 100%);
--card-shadow:
inset 0 2px 2px 1px rgb(32 28 26 / 0.2), 0 1px 2px rgb(0 0 0 / 0.5);
--card-shadow: inset 0 2px 2px 1px rgb(32 28 26 / 0.2), 0 1px 2px rgb(0 0 0 / 0.5);

--card-header: linear-gradient(
180deg,
rgb(255 251 246 / 0.045) 0%,
rgb(255 251 246 / 0.03) 100%
);
--card-header: linear-gradient(180deg, rgb(255 251 246 / 0.045) 0%, rgb(255 251 246 / 0.03) 100%);
--card-header-foreground: var(--color-stone-300);
--card-header-shadow:
inset 0 1px 0 lch(24.13 2.26 37.96), 0 0 2px oklch(0.16 0.01 0);
--card-header-shadow: inset 0 1px 0 lch(24.13 2.26 37.96), 0 0 2px oklch(0.16 0.01 0);

--secondary:
radial-gradient(
120% 55% at 0% 0%,
rgb(255 255 255 / 0.005),
transparent 62%
),
radial-gradient(120% 55% at 0% 0%, rgb(255 255 255 / 0.005), transparent 62%),
linear-gradient(180deg, #1a1817 0%, #0d0c0b 18%, #000000 100%);
--secondary-shadow: 0 0 #0000;

Expand Down
8 changes: 5 additions & 3 deletions frontend/packages/ui/src/theme/theme-provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,11 @@ export function ThemeProvider({ canChoose, defaultTheme, children }: ThemeProvid

const theme: ResolvedTheme = canChoose ? (choice ?? defaultTheme) : defaultTheme;

// Before paint, not after: the pre-paint script can only replay a theme this browser has already
// resolved once, so a first-time visitor starts with no class at all. A passive effect would let
// that first commit paint in the wrong palette and then snap.
/*
* Before paint, not after: the pre-paint script can only replay a theme this browser has already
* resolved once, so a first-time visitor starts with no class at all. A passive effect would let
* that first commit paint in the wrong palette and then snap.
*/
useLayoutEffect(() => {
applyTheme(theme);
mirrorResolvedTheme(theme);
Expand Down
Loading