@activatus/code-standards 0.0.0-stage โ 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +200 -2
- package/docs/Guides/_category_.json +4 -0
- package/docs/Guides/component-library-workflow.md +182 -0
- package/docs/Guides/migrating.md +144 -0
- package/docs/Guides/setup.md +130 -0
- package/docs/Standards/_category_.json +4 -0
- package/docs/Standards/agent-skills.md +125 -0
- package/docs/Standards/agent-specs.md +89 -0
- package/docs/Standards/branch-naming.md +66 -0
- package/docs/Standards/conventional-commits.md +189 -0
- package/docs/Standards/direnv.mdx +249 -0
- package/docs/Standards/documentation.md +311 -0
- package/docs/Standards/editorconfig.md +72 -0
- package/docs/Standards/eslint.md +709 -0
- package/docs/Standards/human-readable-names.md +234 -0
- package/docs/Standards/lint-errors.md +139 -0
- package/docs/Standards/prettier.md +97 -0
- package/docs/Standards/readable-code.md +540 -0
- package/docs/Standards/stylelint.md +178 -0
- package/docs/Standards/styling.md +284 -0
- package/docs/Standards/tech-stack.md +156 -0
- package/docs/Structure/_category_.json +4 -0
- package/docs/Structure/api-routes.md +118 -0
- package/docs/Structure/component-organization.md +519 -0
- package/docs/Structure/folder-structure.md +353 -0
- package/docs/Structure/import-organization.md +158 -0
- package/docs/Structure/naming-conventions.md +336 -0
- package/docs/design-system-components.md +117 -0
- package/docs/intro.md +46 -0
- package/docs/ontology.md +132 -0
- package/docs/templates.mdx +419 -0
- package/docs/version-history.md +211 -0
- package/package.json +56 -3
- package/static/templates/.commitlintrc.json +3 -0
- package/static/templates/.editorconfig +28 -0
- package/static/templates/.envrc +25 -0
- package/static/templates/.nvmrc +1 -0
- package/static/templates/AGENTS.md +160 -0
- package/static/templates/decision-doc.md +19 -0
- package/static/templates/docs/ai/agent-harness.md +17 -0
- package/static/templates/docs/ai/agents.md +17 -0
- package/static/templates/docs/ai/architecture.md +17 -0
- package/static/templates/docs/ai/context.md +17 -0
- package/static/templates/docs/ai/model-routing.md +17 -0
- package/static/templates/docs/ai/models.md +17 -0
- package/static/templates/docs/ai/retrieval.md +17 -0
- package/static/templates/docs/ai/tools.md +17 -0
- package/static/templates/docs/architecture/authentication.md +17 -0
- package/static/templates/docs/architecture/backend.md +17 -0
- package/static/templates/docs/architecture/background-jobs.md +17 -0
- package/static/templates/docs/architecture/backups.md +17 -0
- package/static/templates/docs/architecture/caching.md +17 -0
- package/static/templates/docs/architecture/ci-cd.md +17 -0
- package/static/templates/docs/architecture/disaster-recovery.md +17 -0
- package/static/templates/docs/architecture/environments.md +17 -0
- package/static/templates/docs/architecture/frontend.md +17 -0
- package/static/templates/docs/architecture/hosting.md +17 -0
- package/static/templates/docs/architecture/observability.md +17 -0
- package/static/templates/docs/architecture/realtime.md +17 -0
- package/static/templates/docs/architecture/search.md +17 -0
- package/static/templates/docs/architecture/services.md +17 -0
- package/static/templates/docs/architecture/state-management.md +17 -0
- package/static/templates/docs/architecture/storage.md +17 -0
- package/static/templates/docs/architecture/system.md +17 -0
- package/static/templates/docs/business/buying-trigger.md +17 -0
- package/static/templates/docs/business/customer-value.md +17 -0
- package/static/templates/docs/business/customer.md +17 -0
- package/static/templates/docs/business/defensibility.md +17 -0
- package/static/templates/docs/business/growth.md +17 -0
- package/static/templates/docs/business/pricing.md +17 -0
- package/static/templates/docs/business/revenue-model.md +17 -0
- package/static/templates/docs/business/unit-economics.md +17 -0
- package/static/templates/docs/data/analytics.md +17 -0
- package/static/templates/docs/data/audit.md +17 -0
- package/static/templates/docs/data/database.md +17 -0
- package/static/templates/docs/data/indexing.md +17 -0
- package/static/templates/docs/data/migrations.md +17 -0
- package/static/templates/docs/data/model.md +17 -0
- package/static/templates/docs/data/retention.md +17 -0
- package/static/templates/docs/data/schema.md +17 -0
- package/static/templates/docs/deployment/approval.md +17 -0
- package/static/templates/docs/deployment/live-verification.md +17 -0
- package/static/templates/docs/deployment/process.md +17 -0
- package/static/templates/docs/deployment/rollback.md +17 -0
- package/static/templates/docs/design/design-system.md +17 -0
- package/static/templates/docs/design/errors.md +17 -0
- package/static/templates/docs/design/feedback.md +17 -0
- package/static/templates/docs/design/interactions.md +17 -0
- package/static/templates/docs/design/layout.md +17 -0
- package/static/templates/docs/design/navigation.md +17 -0
- package/static/templates/docs/design/principles.md +17 -0
- package/static/templates/docs/design/responsive.md +17 -0
- package/static/templates/docs/development/branching.md +17 -0
- package/static/templates/docs/development/code-quality.md +17 -0
- package/static/templates/docs/development/coding-standards.md +17 -0
- package/static/templates/docs/development/commits.md +17 -0
- package/static/templates/docs/development/merge-strategy.md +17 -0
- package/static/templates/docs/development/process.md +17 -0
- package/static/templates/docs/development/pull-requests.md +17 -0
- package/static/templates/docs/experience/accessibility.md +17 -0
- package/static/templates/docs/experience/aha-moment.md +17 -0
- package/static/templates/docs/experience/anti-experience.md +17 -0
- package/static/templates/docs/experience/friction.md +17 -0
- package/static/templates/docs/experience/outcomes.md +17 -0
- package/static/templates/docs/experience/principles.md +17 -0
- package/static/templates/docs/experience/retention.md +17 -0
- package/static/templates/docs/experience/sharing.md +17 -0
- package/static/templates/docs/overview/how-it-works.md +17 -0
- package/static/templates/docs/overview/what-is-it.md +17 -0
- package/static/templates/docs/overview/writing-docs.md +16 -0
- package/static/templates/docs/pitch/alternatives.md +17 -0
- package/static/templates/docs/pitch/assumptions.md +17 -0
- package/static/templates/docs/pitch/audience.md +17 -0
- package/static/templates/docs/pitch/concept.md +17 -0
- package/static/templates/docs/pitch/differentiators.md +17 -0
- package/static/templates/docs/pitch/outcomes.md +17 -0
- package/static/templates/docs/pitch/problem.md +17 -0
- package/static/templates/docs/pitch/product-market-fit.md +17 -0
- package/static/templates/docs/pitch/success-criteria.md +17 -0
- package/static/templates/docs/pitch/validation.md +17 -0
- package/static/templates/docs/pitch/value-proposition.md +17 -0
- package/static/templates/docs/pitch/vision.md +17 -0
- package/static/templates/docs/scope/core.md +17 -0
- package/static/templates/docs/scope/exclusions.md +17 -0
- package/static/templates/docs/scope/future.md +17 -0
- package/static/templates/docs/scope/launch.md +17 -0
- package/static/templates/docs/scope/post-launch.md +17 -0
- package/static/templates/docs/scope/priorities.md +17 -0
- package/static/templates/docs/scope/quality-bar.md +17 -0
- package/static/templates/docs/security/audit.md +17 -0
- package/static/templates/docs/security/data-access.md +17 -0
- package/static/templates/docs/security/logging.md +17 -0
- package/static/templates/docs/security/privacy.md +17 -0
- package/static/templates/docs/security/secrets.md +17 -0
- package/static/templates/docs/security/threat-model.md +17 -0
- package/static/templates/docs/testing/end-to-end.md +17 -0
- package/static/templates/docs/testing/integration.md +17 -0
- package/static/templates/docs/testing/regression.md +17 -0
- package/static/templates/docs/testing/security.md +17 -0
- package/static/templates/docs/testing/strategy.md +17 -0
- package/static/templates/docs/testing/test-data.md +17 -0
- package/static/templates/docs/testing/unit.md +17 -0
- package/static/templates/docs/uat/checks.md +17 -0
- package/static/templates/docs/uat/strategy.md +17 -0
- package/static/templates/docs/users/permissions.md +17 -0
- package/static/templates/docs/users/roles.md +17 -0
- package/static/templates/docs/users/user-types.md +17 -0
- package/static/templates/docs/workflows/assignment.md +17 -0
- package/static/templates/docs/workflows/bugfix.md +17 -0
- package/static/templates/docs/workflows/dependencies.md +17 -0
- package/static/templates/docs/workflows/deployment.md +17 -0
- package/static/templates/docs/workflows/development.md +17 -0
- package/static/templates/docs/workflows/planning.md +17 -0
- package/static/templates/docs/workflows/review.md +17 -0
- package/static/templates/docs/workflows/uat.md +17 -0
- package/static/templates/docsSite/docusaurus.config.ts +119 -0
- package/static/templates/docsSite/gitignore.txt +3 -0
- package/static/templates/docsSite/package.json.txt +42 -0
- package/static/templates/docsSite/pnpm-workspace.yaml +2 -0
- package/static/templates/docsSite/sidebars.ts +8 -0
- package/static/templates/docsSite/src/css/custom.css +1 -0
- package/static/templates/docsSite/src/pages/index.tsx +9 -0
- package/static/templates/docsSite/static/images/logo.svg +4 -0
- package/static/templates/docsSite/tsconfig.json +7 -0
- package/static/templates/eslint/base/eslint.config.mjs +79 -0
- package/static/templates/eslint/nextjs/eslint.config.mjs +137 -0
- package/static/templates/eslint/react/eslint.config.mjs +138 -0
- package/static/templates/eslint/typescript/eslint.config.mjs +126 -0
- package/static/templates/globals.css +16 -0
- package/static/templates/husky/commit-msg +1 -0
- package/static/templates/husky/pre-commit +1 -0
- package/static/templates/next.config.mjs +18 -0
- package/static/templates/prettier.config.js +17 -0
- package/static/templates/skills/codeReview.md +31 -0
- package/static/templates/skills/format.md +18 -0
- package/static/templates/skills/lint.md +21 -0
- package/static/templates/skills/lintFix.md +24 -0
- package/static/templates/skills/securityAudit.md +28 -0
- package/static/templates/skills/test.md +24 -0
- package/static/templates/skills/typescript.md +28 -0
- package/static/templates/skills/updateLayout.md +20 -0
- package/static/templates/stylelint/css/stylelint.config.mjs +28 -0
- package/static/templates/stylelint/scss/stylelint.config.mjs +10 -0
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
---
|
|
2
|
+
sidebar_position: 5
|
|
3
|
+
sidebar_label: "Stylelint"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Stylelint Configuration
|
|
7
|
+
|
|
8
|
+
Components are styled with [Tailwind CSS](/Standards/styling), and Stylelint lints the CSS that is still written by hand โ `globals.css` and any CSS Modules. Start from the config below that matches the project:
|
|
9
|
+
|
|
10
|
+
| Config | Use when |
|
|
11
|
+
| --- | --- |
|
|
12
|
+
| [Plain CSS](#plain-css-and-css-modules) | Plain CSS and CSS Modules (`.module.css`) |
|
|
13
|
+
| [SCSS](#scss) | SCSS / Sass |
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pnpm add -D stylelint stylelint-config-standard stylelint-config-recess-order
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
For SCSS projects, install `stylelint-config-standard-scss` in place of `stylelint-config-standard`:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pnpm add -D stylelint stylelint-config-standard-scss stylelint-config-recess-order
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
Create `stylelint.config.mjs` in your project root:
|
|
30
|
+
|
|
31
|
+
### Plain CSS and CSS Modules
|
|
32
|
+
|
|
33
|
+
```js title="stylelint.config.mjs"
|
|
34
|
+
/** @type {import("stylelint").Config} */
|
|
35
|
+
export default {
|
|
36
|
+
ignoreFiles: ["**/vendor/**"],
|
|
37
|
+
extends: ["stylelint-config-standard", "stylelint-config-recess-order"],
|
|
38
|
+
rules: {
|
|
39
|
+
"selector-max-id": 0,
|
|
40
|
+
"property-no-vendor-prefix": true,
|
|
41
|
+
"color-function-notation": "modern",
|
|
42
|
+
"import-notation": "string",
|
|
43
|
+
"at-rule-no-unknown": [
|
|
44
|
+
true,
|
|
45
|
+
{ ignoreAtRules: ["theme", "utility", "variant", "custom-variant", "source", "plugin"] },
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
overrides: [
|
|
49
|
+
{
|
|
50
|
+
files: ["**/*.module.css"],
|
|
51
|
+
rules: {
|
|
52
|
+
"color-named": "never",
|
|
53
|
+
"color-no-hex": true,
|
|
54
|
+
"selector-class-pattern": [
|
|
55
|
+
"^[a-z][a-zA-Z0-9]*$",
|
|
56
|
+
{ message: "Expected class selector to be camelCase" },
|
|
57
|
+
],
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
};
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### SCSS
|
|
65
|
+
|
|
66
|
+
```js title="stylelint.config.mjs"
|
|
67
|
+
/** @type {import("stylelint").Config} */
|
|
68
|
+
export default {
|
|
69
|
+
ignoreFiles: ["**/vendor/**"],
|
|
70
|
+
extends: ["stylelint-config-standard-scss", "stylelint-config-recess-order"],
|
|
71
|
+
rules: {
|
|
72
|
+
"selector-max-id": 0,
|
|
73
|
+
"property-no-vendor-prefix": true,
|
|
74
|
+
"color-function-notation": "modern",
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Project-specific overrides
|
|
80
|
+
|
|
81
|
+
Add to the `rules` block:
|
|
82
|
+
|
|
83
|
+
```js
|
|
84
|
+
export default {
|
|
85
|
+
// ...
|
|
86
|
+
rules: {
|
|
87
|
+
// ...
|
|
88
|
+
// project-specific overrides
|
|
89
|
+
"selector-max-type": 3,
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Add a lint script to `package.json`:
|
|
95
|
+
|
|
96
|
+
```json
|
|
97
|
+
{
|
|
98
|
+
"scripts": {
|
|
99
|
+
"lint:css": "stylelint \"**/*.{css,scss}\"",
|
|
100
|
+
"lint:css:fix": "stylelint \"**/*.{css,scss}\" --fix"
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Extended configs
|
|
106
|
+
|
|
107
|
+
| Config | Used by | Purpose |
|
|
108
|
+
| --- | --- | --- |
|
|
109
|
+
| `stylelint-config-standard` | Plain CSS and CSS Modules | Stylelint's recommended modern CSS rules |
|
|
110
|
+
| `stylelint-config-standard-scss` | SCSS | Standard rules + SCSS-specific rules |
|
|
111
|
+
| `stylelint-config-recess-order` | all | Enforces Bootstrap/RECESS property order |
|
|
112
|
+
|
|
113
|
+
## Property order
|
|
114
|
+
|
|
115
|
+
Properties are sorted using the [RECESS order](https://github.com/stordahl/stylelint-config-recess-order) (the same order used by Bootstrap). The broad grouping:
|
|
116
|
+
|
|
117
|
+
1. Positioning (`position`, `top`, `z-index`, โฆ)
|
|
118
|
+
2. Box model (`display`, `width`, `margin`, `padding`, โฆ)
|
|
119
|
+
3. Typography (`font`, `line-height`, `color`, โฆ)
|
|
120
|
+
4. Visual (`background`, `border`, `opacity`, โฆ)
|
|
121
|
+
5. Misc (`transition`, `animation`, โฆ)
|
|
122
|
+
|
|
123
|
+
## Key rule decisions
|
|
124
|
+
|
|
125
|
+
### `selector-max-id: 0`
|
|
126
|
+
|
|
127
|
+
ID selectors create specificity that is nearly impossible to override without escalating to `!important`. Use classes instead.
|
|
128
|
+
|
|
129
|
+
### `property-no-vendor-prefix: true`
|
|
130
|
+
|
|
131
|
+
Vendor prefixes should be added by **Autoprefixer** in the build pipeline, not written by hand. Hand-written prefixes become stale as browser support improves. Configure Autoprefixer with a Browserslist query instead.
|
|
132
|
+
|
|
133
|
+
### `color-function-notation: "modern"`
|
|
134
|
+
|
|
135
|
+
Enforces `rgb(255 0 0 / 50%)` instead of `rgba(255, 0, 0, 0.5)`. The modern syntax is part of CSS Color Level 4 and is supported in all modern browsers.
|
|
136
|
+
|
|
137
|
+
### `ignoreFiles`: third-party code is exempt
|
|
138
|
+
|
|
139
|
+
CSS in a `vendor/` folder is not ours, so it is not linted. See [Third-party code is exempt](/Standards/eslint#third-party-code-is-exempt).
|
|
140
|
+
|
|
141
|
+
### Tailwind: `import-notation: "string"` and `at-rule-no-unknown`
|
|
142
|
+
|
|
143
|
+
`stylelint-config-standard` rejects `@import "tailwindcss"` and does not know Tailwind's at-rules. These two settings allow the import and the `@theme`, `@utility`, `@variant`, `@custom-variant`, `@source` and `@plugin` at-rules.
|
|
144
|
+
|
|
145
|
+
### Custom properties: kebab-case
|
|
146
|
+
|
|
147
|
+
`stylelint-config-standard` requires CSS variables to be kebab-case (`--color-brand-primary`). See [CSS Styling](/Standards/styling#css-variables).
|
|
148
|
+
|
|
149
|
+
### CSS Modules: `color-named: never` and `color-no-hex`
|
|
150
|
+
|
|
151
|
+
Colours come from the [global colour variables](/Standards/styling#-colours-always-come-from-global-variables). A CSS Module may not contain `white` or `#3b82f6` โ it references `var(--color-white)` or `var(--color-brand-primary)`. Colour values are only written in `globals.css`.
|
|
152
|
+
|
|
153
|
+
### CSS Modules: `selector-class-pattern: camelCase`
|
|
154
|
+
|
|
155
|
+
CSS Modules are consumed in JavaScript as `styles.myClass`. Kebab-case names require bracket notation (`styles["my-class"]`), which is easy to mistype. camelCase names allow dot notation and are consistent with JavaScript naming conventions.
|
|
156
|
+
|
|
157
|
+
### SCSS: `scss/load-no-partial-leading-underscore`
|
|
158
|
+
|
|
159
|
+
Leading underscores on `@import` paths are a Sass 1.x convention for marking partials. With `@use` and `@forward` (the modern Sass module system), the underscore is handled automatically โ including it manually in `@use` paths is an error. `stylelint-config-standard-scss` enforces this.
|
|
160
|
+
|
|
161
|
+
## Editor integration
|
|
162
|
+
|
|
163
|
+
### VS Code
|
|
164
|
+
|
|
165
|
+
Install the [Stylelint](https://marketplace.visualstudio.com/items?itemName=stylelint.vscode-stylelint) extension, then add to workspace settings:
|
|
166
|
+
|
|
167
|
+
```json
|
|
168
|
+
{
|
|
169
|
+
"stylelint.validate": ["css", "scss"],
|
|
170
|
+
"editor.codeActionsOnSave": {
|
|
171
|
+
"source.fixAll.stylelint": "explicit"
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### JetBrains IDEs
|
|
177
|
+
|
|
178
|
+
Go to **Settings โ Languages & Frameworks โ Style Sheets โ Stylelint** and enable it.
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
---
|
|
2
|
+
sidebar_position: 10
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# CSS Styling
|
|
6
|
+
|
|
7
|
+
Standards for styling React components and applications.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## ๐จ Tailwind CSS
|
|
12
|
+
|
|
13
|
+
Use Tailwind CSS utility classes for all component styling. Tailwind is compatible with React Server Components and is the foundation [shadcn/ui](/Guides/component-library-workflow) is built on.
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
// Button.tsx
|
|
17
|
+
export const Button = () => {
|
|
18
|
+
return (
|
|
19
|
+
<button className="rounded-md bg-brand-primary px-8 py-4 text-white transition-colors hover:bg-brand-primary-hover">
|
|
20
|
+
Click me
|
|
21
|
+
</button>
|
|
22
|
+
);
|
|
23
|
+
};
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## ๐ Layout: Flexbox First
|
|
29
|
+
|
|
30
|
+
**Build every layout with flexbox.** Reach for `flex` first, and split the layout into child components so each one styles only itself.
|
|
31
|
+
|
|
32
|
+
**Only use CSS grid when it is specifically asked for.**
|
|
33
|
+
|
|
34
|
+
### โ
Flex parent, child components
|
|
35
|
+
|
|
36
|
+
The parent arranges its children with flex. Each child is its own component with its own styles:
|
|
37
|
+
|
|
38
|
+
```tsx title="pageHeader/PageHeader.tsx"
|
|
39
|
+
import { PageHeaderActions } from "./pageHeaderActions";
|
|
40
|
+
import { PageHeaderTitle } from "./pageHeaderTitle";
|
|
41
|
+
|
|
42
|
+
export const PageHeader = () => {
|
|
43
|
+
return (
|
|
44
|
+
<header className="flex items-center justify-between gap-4">
|
|
45
|
+
<PageHeaderTitle />
|
|
46
|
+
<PageHeaderActions />
|
|
47
|
+
</header>
|
|
48
|
+
);
|
|
49
|
+
};
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
```tsx title="pageHeader/pageHeaderActions/PageHeaderActions.tsx"
|
|
53
|
+
export const PageHeaderActions = () => {
|
|
54
|
+
return (
|
|
55
|
+
<div className="flex items-center gap-2">
|
|
56
|
+
<button className="rounded-md bg-brand-primary px-4 py-2 text-white">
|
|
57
|
+
Save
|
|
58
|
+
</button>
|
|
59
|
+
<button className="rounded-md px-4 py-2 text-text">Cancel</button>
|
|
60
|
+
</div>
|
|
61
|
+
);
|
|
62
|
+
};
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### โ One component styling everything
|
|
66
|
+
|
|
67
|
+
```tsx
|
|
68
|
+
// โ Bad โ grid without being asked, and every piece styled in one place
|
|
69
|
+
export const PageHeader = () => {
|
|
70
|
+
return (
|
|
71
|
+
<header className="grid grid-cols-[1fr_auto] items-center gap-4">
|
|
72
|
+
<div className="flex flex-col gap-1">
|
|
73
|
+
<h1 className="text-2xl font-semibold text-text">Invoices</h1>
|
|
74
|
+
<p className="text-sm text-text">All invoices for this account</p>
|
|
75
|
+
</div>
|
|
76
|
+
<div className="flex items-center gap-2">
|
|
77
|
+
<button className="rounded-md bg-brand-primary px-4 py-2 text-white">
|
|
78
|
+
Save
|
|
79
|
+
</button>
|
|
80
|
+
<button className="rounded-md px-4 py-2 text-text">Cancel</button>
|
|
81
|
+
</div>
|
|
82
|
+
</header>
|
|
83
|
+
);
|
|
84
|
+
};
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Rules
|
|
88
|
+
|
|
89
|
+
- **Flex first.** `flex`, `flex-col`, `gap-*`, `items-*` and `justify-*` cover almost every layout.
|
|
90
|
+
- **The parent positions, the child styles itself.** A parent sets direction, spacing and alignment. Each child component owns its own padding, colour and typography.
|
|
91
|
+
- **Split into child components** rather than growing one long `className` or a deep tree of `div`s. Each child gets [its own folder](/Structure/component-organization#nested-components).
|
|
92
|
+
- **Space with `gap`**, not margins on the children.
|
|
93
|
+
- **Grid only when asked.** Do not choose CSS grid on your own โ use it only when the task specifically asks for a grid.
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## โ Avoid These
|
|
98
|
+
|
|
99
|
+
- โ **CSS grid**, unless it was specifically asked for
|
|
100
|
+
|
|
101
|
+
- โ **Hard-coded colours** โ hex, `rgb()` or named colours anywhere outside the theme
|
|
102
|
+
- โ **Arbitrary colour values** (`bg-[#3b82f6]`) โ if the colour is not in the palette, it is not used
|
|
103
|
+
- โ **Inline styles** (except for dynamic values)
|
|
104
|
+
- โ **Styled-components or emotion** (not compatible with RSC)
|
|
105
|
+
- โ **Global CSS files for component styles**
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## โ
Colours Always Come From Global Variables
|
|
110
|
+
|
|
111
|
+
**Strictly follow the global colour palette.** Every colour is defined **once**, as a global colour variable in the theme. Components never contain a colour value โ they reference a variable.
|
|
112
|
+
|
|
113
|
+
The palette is two things, and nothing else:
|
|
114
|
+
|
|
115
|
+
- **Tailwind's built-in colours** (`bg-white`, `text-slate-900`, `border-blue-500`) โ allowed
|
|
116
|
+
- **Project colours** added to the `@theme` block (`bg-brand-primary`)
|
|
117
|
+
|
|
118
|
+
A colour that is in neither is not used. If a design calls for a new colour, add it to the global palette first โ never write a one-off value in a component.
|
|
119
|
+
|
|
120
|
+
### โ
Define the colours
|
|
121
|
+
|
|
122
|
+
Each project defines its colours once, in its global stylesheet:
|
|
123
|
+
|
|
124
|
+
```css
|
|
125
|
+
/* app/globals.css */
|
|
126
|
+
@import "tailwindcss";
|
|
127
|
+
|
|
128
|
+
@theme {
|
|
129
|
+
--color-brand-primary: #3b82f6;
|
|
130
|
+
--color-brand-primary-hover: #2563eb;
|
|
131
|
+
--color-brand-secondary: #64748b;
|
|
132
|
+
--color-surface: #fff;
|
|
133
|
+
--color-text: #0f172a;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
[data-theme="dark"] {
|
|
137
|
+
--color-brand-primary: #60a5fa;
|
|
138
|
+
--color-brand-primary-hover: #93c5fd;
|
|
139
|
+
--color-surface: #0f172a;
|
|
140
|
+
--color-text: #f8fafc;
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Tailwind turns every `--color-*` variable in `@theme` into utility classes: `--color-brand-primary` becomes `bg-brand-primary`, `text-brand-primary`, `border-brand-primary` and so on.
|
|
145
|
+
|
|
146
|
+
The project's design decisions โ what each colour is for, the fonts, the spacing โ are recorded in its `docs/design/design-system.md` [Decision Doc](/Standards/documentation).
|
|
147
|
+
|
|
148
|
+
### โ
Use the colours
|
|
149
|
+
|
|
150
|
+
```tsx
|
|
151
|
+
// โ
Good โ colours come from the theme
|
|
152
|
+
<p className="bg-surface text-text">Hello</p>
|
|
153
|
+
<button className="bg-brand-primary text-white">Save</button>
|
|
154
|
+
|
|
155
|
+
// โ Bad โ colours hard-coded in the component
|
|
156
|
+
<p className="bg-[#fff] text-[#0f172a]">Hello</p>
|
|
157
|
+
<button style={{ background: "#3b82f6", color: "white" }}>Save</button>
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
When you do write CSS by hand, reference the variable โ never the value:
|
|
161
|
+
|
|
162
|
+
```css
|
|
163
|
+
/* โ
Good */
|
|
164
|
+
.chart {
|
|
165
|
+
color: var(--color-white);
|
|
166
|
+
background: var(--color-brand-primary);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/* โ Bad */
|
|
170
|
+
.chart {
|
|
171
|
+
color: white;
|
|
172
|
+
background: #3b82f6;
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Tailwind's built-in colours are variables too โ `white` is `var(--color-white)`.
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## ๐ก Best Practices
|
|
181
|
+
|
|
182
|
+
### Tailwind utilities
|
|
183
|
+
|
|
184
|
+
- **Use for**: All component styling
|
|
185
|
+
- **Layout**: Flexbox first; grid only when specifically asked
|
|
186
|
+
- **Colours**: Global palette only โ Tailwind colours and project colours from `@theme` โ never arbitrary values
|
|
187
|
+
- **Long class lists**: Extract a component, not a CSS class
|
|
188
|
+
- **Conditional classes**: Use `clsx`
|
|
189
|
+
- **Class order**: Sorted automatically by [Prettier](/Standards/prettier#tailwind-class-sorting) โ do not sort by hand
|
|
190
|
+
|
|
191
|
+
### Global CSS
|
|
192
|
+
|
|
193
|
+
- **Use for**: The Tailwind import, the `@theme` block, theme overrides, global resets
|
|
194
|
+
- **Location**: `app/globals.css`
|
|
195
|
+
- **Avoid**: Component-specific styles
|
|
196
|
+
- **Import only**: In the root layout
|
|
197
|
+
|
|
198
|
+
### CSS Modules
|
|
199
|
+
|
|
200
|
+
- **Use for**: The rare style Tailwind cannot express (complex keyframe animations, third-party overrides)
|
|
201
|
+
- **File naming**: `componentName.module.css`
|
|
202
|
+
- **Colours**: `var(--color-*)` only โ Stylelint rejects hex and named colours in CSS Modules
|
|
203
|
+
- **Import**: Co-located with component
|
|
204
|
+
|
|
205
|
+
### Inline Styles
|
|
206
|
+
|
|
207
|
+
- **Use for**: Dynamic values only (e.g., `style={{ height: sliderValue }}`)
|
|
208
|
+
- **Avoid**: Static styles that should be Tailwind utilities
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
## ๐ File Organization
|
|
213
|
+
|
|
214
|
+
### Component Styles
|
|
215
|
+
|
|
216
|
+
Styles live in the component's `className` โ there is no separate style file:
|
|
217
|
+
|
|
218
|
+
```text
|
|
219
|
+
components/
|
|
220
|
+
โโโ button/
|
|
221
|
+
โโโ Button.tsx
|
|
222
|
+
โโโ Button.test.tsx
|
|
223
|
+
โโโ index.ts
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Global Styles
|
|
227
|
+
|
|
228
|
+
```text
|
|
229
|
+
app/
|
|
230
|
+
โโโ globals.css # Tailwind import, @theme colour variables, resets
|
|
231
|
+
โโโ layout.tsx
|
|
232
|
+
โโโ page.tsx
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## ๐ฏ Naming Conventions
|
|
238
|
+
|
|
239
|
+
### CSS Variables
|
|
240
|
+
|
|
241
|
+
**Use kebab-case for variable names.** This is the CSS convention, it is what Tailwind's theme namespaces (`--color-*`, `--spacing-*`, `--font-*`) require, and Stylelint enforces it.
|
|
242
|
+
|
|
243
|
+
```css
|
|
244
|
+
/* โ
Good */
|
|
245
|
+
--color-brand-primary: #3b82f6;
|
|
246
|
+
--spacing-unit: 0.25rem;
|
|
247
|
+
--radius-card: 0.5rem;
|
|
248
|
+
|
|
249
|
+
/* โ Bad */
|
|
250
|
+
--colorBrandPrimary: #3b82f6;
|
|
251
|
+
--spacing_unit: 0.25rem;
|
|
252
|
+
--RadiusCard: 0.5rem;
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
Name colours by **role**, not by value: `--color-brand-primary`, not `--color-blue`.
|
|
256
|
+
|
|
257
|
+
### CSS Modules
|
|
258
|
+
|
|
259
|
+
**Use camelCase for class names**, because they are read in JavaScript as `styles.primaryButton`:
|
|
260
|
+
|
|
261
|
+
```css
|
|
262
|
+
/* โ
Good */
|
|
263
|
+
.button {
|
|
264
|
+
}
|
|
265
|
+
.primaryButton {
|
|
266
|
+
}
|
|
267
|
+
.submitButton {
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/* โ Bad */
|
|
271
|
+
.primary-button {
|
|
272
|
+
}
|
|
273
|
+
.submit_button {
|
|
274
|
+
}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
## ๐ Related Documentation
|
|
280
|
+
|
|
281
|
+
- [Stylelint](/Standards/stylelint) - Linting for hand-written CSS
|
|
282
|
+
- [Component Organization](/Structure/component-organization) - How to organize component files
|
|
283
|
+
- [Folder Structure](/Structure/folder-structure) - Overall project structure
|
|
284
|
+
- [Naming Conventions](/Structure/naming-conventions) - File and folder naming rules
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
---
|
|
2
|
+
sidebar_position: 11
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Tech Stack
|
|
6
|
+
|
|
7
|
+
Standard technology choices for Activatus projects.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## ๐ Standard Stack
|
|
12
|
+
|
|
13
|
+
- **Framework**: Next.js 16 (App Router)
|
|
14
|
+
- **Language**: TypeScript 7
|
|
15
|
+
- **Package Manager**: pnpm (monorepo: workspaces)
|
|
16
|
+
- **Styling**: Tailwind CSS
|
|
17
|
+
- **Node Version**: `^22.11 || ^24 || >=26`
|
|
18
|
+
- **Build Tool**: Turbopack (Next.js built-in)
|
|
19
|
+
|
|
20
|
+
### TypeScript 7
|
|
21
|
+
|
|
22
|
+
TypeScript 7 is the compiler rewritten in Go. It is a drop-in replacement for
|
|
23
|
+
the JavaScript implementation for compiling and type-checking, but two things
|
|
24
|
+
change for tooling:
|
|
25
|
+
|
|
26
|
+
- **There is no `tsserver`.** The compiler is its own language server, started
|
|
27
|
+
with `tsc --lsp --stdio`. Editors and tools that drove TypeScript through
|
|
28
|
+
`typescript-language-server` have nothing to drive; they talk to `tsc`
|
|
29
|
+
directly instead.
|
|
30
|
+
- **It answers diagnostics on request.** TypeScript 7 supports LSP pull
|
|
31
|
+
diagnostics, so a client that only waits for a `publishDiagnostics` push can
|
|
32
|
+
sit there believing a file is clean.
|
|
33
|
+
|
|
34
|
+
### Parameterise a library's generics
|
|
35
|
+
|
|
36
|
+
A generic whose default is a permissive index signature returns `any` at every use, and
|
|
37
|
+
the code reads as though it were typed. Storyblok's story type is the one to watch:
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
// storyblok-js-client
|
|
41
|
+
interface ISbStoryData<Content = ISbComponentType<string> & { [index: string]: any }> {
|
|
42
|
+
content: Content;
|
|
43
|
+
// โฆ
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Left bare, `ISbStoryData` makes `story.content` that `any`-indexed fallback. Every field
|
|
48
|
+
access resolves, nothing is checked, and a renamed block field fails in the browser
|
|
49
|
+
rather than in CI. Declare the block and pass it:
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
export interface HeroBlokType extends ISbComponentType<"hero"> {
|
|
53
|
+
headline: string;
|
|
54
|
+
subhead?: string;
|
|
55
|
+
callToAction?: CallToActionBlokType[];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// story.content.headline is string; story.content.headlien is a compile error.
|
|
59
|
+
const story: ISbStoryData<HeroBlokType> = await getStory();
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The parameter flows, so the surrounding types carry it too โ `ISbResult`'s
|
|
63
|
+
`data.stories` becomes `ISbStoryData<HeroBlokType>[]`. Where a story can hold one of several
|
|
64
|
+
blocks, make `Content` a discriminated union on `component` and switch on it.
|
|
65
|
+
|
|
66
|
+
Extend the published type rather than restating its fields: `ISbComponentType<"hero">`
|
|
67
|
+
already contributes `_uid`, `component` and `_editable`, and a hand-written copy drifts
|
|
68
|
+
the moment the library changes.
|
|
69
|
+
|
|
70
|
+
Read every library generic this way. An unparameterised one with a permissive default is
|
|
71
|
+
usually a silent `any`, not a sensible fallback. The [`/typescript` skill](/Standards/agent-skills)
|
|
72
|
+
applies this as a rule.
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## ๐งช Testing Stack
|
|
77
|
+
|
|
78
|
+
- **Unit Testing**: Vitest
|
|
79
|
+
- **Component Testing**: @testing-library/react
|
|
80
|
+
- **E2E Testing**: Playwright
|
|
81
|
+
- **Coverage**: @vitest/coverage-v8
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## ๐๏ธ Database & Backend
|
|
86
|
+
|
|
87
|
+
- **Database**: Supabase (PostgreSQL)
|
|
88
|
+
- **ORM**: Supabase client (direct)
|
|
89
|
+
- **Validation**: Zod schemas
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## โ๏ธ State Management
|
|
94
|
+
|
|
95
|
+
- **Server State**: @tanstack/react-query
|
|
96
|
+
- **Global State**: Zustand
|
|
97
|
+
- **Form State**: @tanstack/react-form
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## ๐ฆ Package Versions
|
|
102
|
+
|
|
103
|
+
See `package.json` for specific version requirements. Key dependencies:
|
|
104
|
+
|
|
105
|
+
```json
|
|
106
|
+
{
|
|
107
|
+
"dependencies": {
|
|
108
|
+
"next": "^16.3.5",
|
|
109
|
+
"react": "^19.3.0",
|
|
110
|
+
"@tanstack/react-query": "^5.85.5",
|
|
111
|
+
"zustand": "^5.0.7"
|
|
112
|
+
},
|
|
113
|
+
"devDependencies": {
|
|
114
|
+
"typescript": "^7.0.2",
|
|
115
|
+
"vitest": "^2.1.8",
|
|
116
|
+
"@testing-library/react": "^16.1.0",
|
|
117
|
+
"playwright": "^1.63.0"
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## ๐จ UI Libraries
|
|
125
|
+
|
|
126
|
+
- **Design System**: Custom (Radix UI primitives)
|
|
127
|
+
- **Icons**: Lucide React
|
|
128
|
+
- **Forms**: React Hook Form
|
|
129
|
+
- **Tables**: TanStack Table
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## ๐ง Development Tools
|
|
134
|
+
|
|
135
|
+
- **Linting**: ESLint 9+
|
|
136
|
+
- **Formatting**: Prettier
|
|
137
|
+
- **Git Hooks**: Husky
|
|
138
|
+
- **Monorepo**: Turborepo
|
|
139
|
+
- **Documentation**: Docusaurus
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## Version Requirements
|
|
144
|
+
|
|
145
|
+
### Node.js
|
|
146
|
+
|
|
147
|
+
- **Consumer minimum**: 20.0.0, matching the packages' `engines` requirement
|
|
148
|
+
- **Repository version**: 24, pinned in `.nvmrc`
|
|
149
|
+
- **Package manager minimum for consumers**: pnpm 9, matching the root `engines` requirement
|
|
150
|
+
- **Repository package manager**: pnpm 11.23.0, pinned in `package.json#packageManager`
|
|
151
|
+
|
|
152
|
+
### TypeScript
|
|
153
|
+
|
|
154
|
+
- **Version**: 5.0+
|
|
155
|
+
- **Strict Mode**: Enabled
|
|
156
|
+
- **Target**: ES2022+
|