Skip to content
Merged
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
51 changes: 51 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ In the repository now:
- A seed script that loads the catalog from `data/` into the database
- ESLint and Prettier for linting and formatting, for both the frontend and the backend
- Vitest for testing
- Tailwind CSS, shadcn/ui and Lucide icons for the interface, see [UI components](#ui-components-shadcnui-tailwind-css-and-lucide)

Planned:

Expand All @@ -88,6 +89,56 @@ We also considered MongoDB and SQLite:

The ordered list of elements in a routine is stored as a join table with a position column, which keeps the foreign keys. How each list in the data model is stored is described in "Storage in PostgreSQL" in [docs/DATA_MODEL.md](docs/DATA_MODEL.md#storage-in-postgresql).

### UI components: shadcn/ui, Tailwind CSS and Lucide

The interface is built from [shadcn/ui](https://ui.shadcn.com) components, styled with [Tailwind CSS](https://tailwindcss.com) 4, with [Lucide](https://lucide.dev) icons.

**Why**

- **Accessibility.** The components are built on [Base UI](https://base-ui.com) primitives, which handle keyboard use, focus and ARIA. We do not write that ourselves, and universal design is a requirement of the course.
- **The code is ours.** shadcn/ui copies the components into `src/components/ui/`. We can read and change them, and only the components we use end up in the bundle. Lucide icons are imported one by one, and unused icons are removed from the bundle.
- **One look.** The same buttons, inputs and selects everywhere.

**How to add a component**

```bash
npx shadcn@latest add dialog
```

The component is added to `src/components/ui/`. Import it with the `@/` alias (`@/` means `src/`):

```tsx
import { SearchIcon } from 'lucide-react'
import { Button } from '@/components/ui/button'

;<Button variant="outline">
<SearchIcon /> Search
</Button>
```

An icon without text needs a label for screen readers, for example `<Button aria-label="Search" size="icon"><SearchIcon /></Button>`.

**Tailwind CSS**

- **Set up.** `tailwindcss` and `@tailwindcss/vite` are in `devDependencies`, because they are only needed to build. The Vite plugin is in `vite.config.ts`, and `src/index.css` starts with `@import 'tailwindcss';`. There is no `tailwind.config.js`: Tailwind 4 is configured in CSS, with [`@theme`](https://tailwindcss.com/docs/theme).
- **Small CSS.** Only the classes that the code uses end up in the built CSS. We checked it: a class that is used is in the built file, and a class that is not used is not.
- **Browsers.** Tailwind 4 is made for Chrome 111, Safari 16.4 and Firefox 128 or newer. Older browsers are not supported.
- **Our own CSS wins over utility classes.** Tailwind puts its styles in CSS layers, and CSS that is not in a layer beats CSS that is, whatever the specificity. A rule in `src/index.css` or `src/App.css` therefore overrides a utility class on the same element. If a class seems to have no effect, look for a plain CSS rule for the same property. New styles should be written as utility classes, or inside `@layer`.
- **Preflight.** Tailwind's base styles reset margins, heading sizes and buttons. Our own CSS is not in a layer, so it still wins where it sets the same property.

**Choices we made**

- **Style.** `base-nova` on Base UI, which is the default of the shadcn/ui CLI. Radix UI is the older alternative.
- **Class names.** The `cn` helper from the `cn` package, as the CLI sets it up.
- **Fonts.** System fonts only. The CLI wanted to add the Geist font (about 60 kB of font files), which we removed.
- **Dark mode.** It follows `data-theme="dark"` on the page, as in `src/styles/variables.css`, and not a `.dark` class.
- **`@/` alias.** Set with `paths` only. TypeScript 6 deprecates `baseUrl`, which the shadcn/ui guide still tells us to add.
- **Lint.** The Fast Refresh rule is off for `src/components/ui/`, because the components also export helpers such as `buttonVariants`.

**Still to do**

- **Colours.** shadcn/ui uses its own tokens (`--background`, `--foreground`, `--primary` …) in `src/index.css`. Our design tokens in `src/styles/variables.css` are separate, and are not connected to them yet. The two tokens `--border` and `--accent` also exist in the Vite starter styles in `src/index.css`, and shadcn/ui's values win. The starter styles go when the page is built.

## Getting started

> The frontend in `src/` is still the Vite starter project, so `npm run dev` only shows the starter page. To work on the backend, also follow "Database (local setup)" below.
Expand Down
25 changes: 25 additions & 0 deletions components.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "base-nova",
"rsc": false,
"tsx": true,
"tailwind": {
"config": "",
"css": "src/index.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"iconLibrary": "lucide",
"rtl": false,
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
},
"menuColor": "default",
"menuAccent": "subtle",
"registries": {}
}
16 changes: 16 additions & 0 deletions docs/AI_USAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,3 +145,19 @@
the checks on a local database: two runs in a row with no changes, the row
counts against the JSON files, broken copies of the data, the soft delete
and clean-up, and that user data is untouched.

## shadcn/ui, Tailwind CSS and Lucide (`components.json`, `src/components/ui/`, README)

- **Tool:** Claude Code
- **What the team did:** Decided to use Tailwind CSS, shadcn/ui and Lucide for
the interface, and wrote issues #51 and #54 with the decisions that had to
be made. The team reviews the choices in the README.
- **What Claude did:** Checked that the shadcn/ui CLI works with our versions
of Vite, TypeScript and React in a scratch copy, then ran `shadcn init` in
the repository. Claude removed the Geist font, made dark mode follow
`data-theme="dark"`, set the `@/` alias with `paths` only, switched off the
Fast Refresh lint rule for the generated folder, wrote a test that renders a
Button with an icon, and wrote the README section. For Tailwind, Claude
checked in a build that a used class ends up in the CSS and an unused class
does not, and checked the browser support and the cascade layers in the
Tailwind documentation. Claude checked lint, build and tests.
6 changes: 6 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,11 @@ export default defineConfig([
globals: globals.node,
},
},
{
// The shadcn/ui CLI copies components into this folder. They also export
// helpers such as buttonVariants, which the Fast Refresh rule does not allow.
files: ['src/components/ui/**'],
rules: { 'react-refresh/only-export-components': 'off' },
},
eslintConfigPrettier,
])
Loading