Skip to content

Commit f1910d1

Browse files
docs: odd prime numbers rule.
1 parent 68e390a commit f1910d1

5 files changed

Lines changed: 277 additions & 489 deletions

File tree

README.md

Lines changed: 25 additions & 218 deletions
Original file line numberDiff line numberDiff line change
@@ -1,246 +1,53 @@
1-
# [`@knighted/css`](https://github.com/knightedcodemonkey/css)
1+
# [`@knighted/css`](https://www.npmjs.com/package/@knighted/css)
22

33
![CI](https://github.com/knightedcodemonkey/css/actions/workflows/ci.yml/badge.svg)
44
[![codecov](https://codecov.io/gh/knightedcodemonkey/css/graph/badge.svg?token=q93Qqwvq6l)](https://codecov.io/gh/knightedcodemonkey/css)
55
[![NPM version](https://img.shields.io/npm/v/@knighted/css.svg)](https://www.npmjs.com/package/@knighted/css)
66

7-
`@knighted/css` walks your JavaScript/TypeScript module graph, compiles every CSS-like dependency (plain CSS, Sass/SCSS, Less, vanilla-extract), and ships both the concatenated stylesheet string and optional `.knighted-css.*` imports that keep selectors typed. Use it when you need fully materialized styles ahead of runtime—Shadow DOM surfaces, server-rendered routes, static site builds, or any entry point that should inline CSS without spinning up a full bundler.
7+
`@knighted/css` is a zero-bundler CSS pipeline for JavaScript and TypeScript projects. Point it at an entry module and it walks the graph, compiles every CSS-like dependency (CSS, Sass/SCSS, Less, vanilla-extract), and hands back both a concatenated stylesheet string and optional `.knighted-css.*` selector manifests for type-safe loaders.
88

9-
## Why
9+
## What it does (at a glance)
1010

11-
I needed a single source of truth for UI components that could drop into both light DOM pages and Shadow DOM hosts, without losing encapsulated styling in the latter.
11+
- **Graph walking**: Follows `import` trees the same way Node does (tsconfig `paths`, package `exports`/`imports`, hash specifiers, etc.) using [`oxc-resolver`](https://github.com/oxc-project/oxc-resolver).
12+
- **Multi-dialect compilation**: Runs Sass, Less, Lightning CSS, or vanilla-extract integrations on demand so every dependency ends up as plain CSS.
13+
- **Loader + CLI**: Ship CSS at runtime via `?knighted-css` loader queries or ahead of time via the `css()` API and the `knighted-css-generate-types` command.
14+
- **Shadow DOM + SSR ready**: Inline styles in server renders, ship them alongside web components, or keep classic DOM apps in sync—all without wiring a full bundler.
1215

13-
## Quick Links
16+
See the [docs/](./docs) directory for deep dives on loaders, type generation, specificity boosts, Sass aliases, and the combined import queries.
1417

15-
- [Features](#features)
16-
- [Requirements](#requirements)
17-
- [Installation](#installation)
18-
- [Quick Start](#quick-start)
19-
- [API](#api)
20-
- [Entry points (`import`)](#entry-points-at-a-glance)
21-
- [Examples](#examples)
22-
- [Demo](#demo)
18+
## Workspaces in this repo
2319

24-
## Features
20+
| Workspace | NPM Name | What it contains |
21+
| --------------------- | -------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
22+
| `packages/css` | [`@knighted/css`](https://www.npmjs.com/package/@knighted/css) | The production library: graph walker, compilation pipeline, loader helpers, CLI, and docs. Published to npm and meant for real builds. |
23+
| `packages/playwright` | `@knighted/css-playwright-fixture` | The end-to-end demo + regression suite. Playwright drives Lit + React examples, hash-import workspace scenarios, and SSR checks to ensure the core package keeps working across bundlers. |
2524

26-
- Traverses module graphs with a built-in walker to find transitive style imports (no bundler required).
27-
- Resolution parity via [`oxc-resolver`](https://github.com/oxc-project/oxc-resolver): tsconfig `paths`, package `exports` + `imports`, and extension aliasing (e.g., `.css.js``.css.ts`) are honored without wiring up a bundler.
28-
- Compiles `*.css`, `*.scss`, `*.sass`, `*.less`, and `*.css.ts` (vanilla-extract) files out of the box.
29-
- Optional post-processing via [`lightningcss`](https://github.com/parcel-bundler/lightningcss) for minification, prefixing, media query optimizations, or specificity boosts.
30-
- Pluggable resolver/filter hooks for custom module resolution (e.g., Rspack/Vite/webpack aliases) or selective inclusion.
31-
- First-class loader (`@knighted/css/loader`) so bundlers can import compiled CSS alongside their modules via `?knighted-css`.
32-
- Built-in type generation CLI (`knighted-css-generate-types`) that emits `.knighted-css.*` selector manifests so TypeScript gets literal tokens in lockstep with the loader exports.
25+
Each workspace is a standalone npm project. Run commands from the repo root with `npm run <script> -w <workspace>` or `npm run <script> --workspaces` to fan out when needed.
3326

34-
## Requirements
35-
36-
- Node.js `>= 22.17.0`
37-
- npm `>= 10.9.0`
38-
- Install peer toolchains you intend to use (`sass`, `less`, `@vanilla-extract/integration`, etc.).
39-
40-
## Installation
41-
42-
```bash
43-
npm install @knighted/css
44-
```
45-
46-
Install the peers your project is using, for example `less`, or `sass`, etc.
47-
48-
## Quick Start
27+
## Quick start
4928

5029
```ts
51-
// scripts/extract-styles.ts
5230
import { css } from '@knighted/css'
5331

54-
const styles = await css('./src/components/app.ts', {
32+
const sheet = await css('./src/entry.tsx', {
5533
cwd: process.cwd(),
5634
lightningcss: { minify: true },
5735
})
5836

59-
console.log(styles)
60-
```
61-
62-
Run it with `tsx`/`node` and you will see a fully inlined stylesheet for `app.ts` and every style import it references, regardless of depth.
63-
64-
## API
65-
66-
```ts
67-
type CssOptions = {
68-
extensions?: string[] // customize file extensions to scan
69-
cwd?: string // working directory (defaults to process.cwd())
70-
filter?: (filePath: string) => boolean
71-
lightningcss?: boolean | LightningTransformOptions
72-
specificityBoost?: {
73-
visitor?: LightningTransformOptions<never>['visitor']
74-
strategy?: SpecificityStrategy
75-
match?: SpecificitySelector[]
76-
}
77-
moduleGraph?: ModuleGraphOptions
78-
resolver?: (
79-
specifier: string,
80-
ctx: { cwd: string; from?: string },
81-
) => string | Promise<string | undefined>
82-
peerResolver?: (name: string) => Promise<unknown> // for custom module loading
83-
}
84-
85-
async function css(entry: string, options?: CssOptions): Promise<string>
86-
```
87-
88-
## Entry points at a glance
89-
90-
### Runtime loader hook (`?knighted-css`)
91-
92-
Import any module with the `?knighted-css` query to receive the compiled stylesheet string:
93-
94-
```ts
95-
import { knightedCss } from './button.js?knighted-css'
96-
```
97-
98-
See [docs/loader.md](./docs/loader.md) for the full configuration, combined imports, and `&types` runtime selector map guidance.
99-
100-
### Type generation hook (`*.knighted-css*`)
101-
102-
Run `knighted-css-generate-types` so every specifier that ends with `.knighted-css` produces a sibling manifest containing literal selector tokens:
103-
104-
```ts
105-
import stableSelectors from './button.module.scss.knighted-css.js'
106-
```
107-
108-
Refer to [docs/type-generation.md](./docs/type-generation.md) for CLI options and workflow tips.
109-
110-
### Combined + runtime selectors
111-
112-
Need the module exports, `knightedCss`, and a runtime `stableSelectors` map from one import? Use `?knighted-css&combined&types` (plus optional `&named-only`). Example:
113-
114-
```ts
115-
import type { KnightedCssCombinedModule } from '@knighted/css/loader'
116-
import { asKnightedCssCombinedModule } from '@knighted/css/loader-helpers'
117-
import type { ButtonStableSelectors } from './button.css.knighted-css.js'
118-
import * as buttonModule from './button.js?knighted-css&combined&types'
119-
120-
const {
121-
default: Button,
122-
knightedCss,
123-
stableSelectors,
124-
} = asKnightedCssCombinedModule<
125-
typeof import('./button.js'),
126-
{ stableSelectors: Readonly<Record<keyof ButtonStableSelectors, string>> }
127-
>(buttonModule)
128-
129-
stableSelectors.shell
130-
```
131-
132-
> [!NOTE]
133-
> `stableSelectors` here is for runtime use; TypeScript still reads literal tokens from the generated `.knighted-css.*` modules. For a full decision matrix, see [docs/combined-queries.md](./docs/combined-queries.md).
134-
> Prefer importing `asKnightedCssCombinedModule` from `@knighted/css/loader-helpers` instead of grabbing it from `@knighted/css/loader`the helper lives in a Node-free chunk so both browser and server bundles stay happy.
135-
136-
## Examples
137-
138-
- [Generate standalone stylesheets](#generate-standalone-stylesheets)
139-
- [Inline CSS during SSR](#inline-css-during-ssr)
140-
- [Custom resolver](#custom-resolver-enhanced-resolve-example)
141-
- [Specificity boost](#specificity-boost)
142-
- [Bundler loader](./docs/loader.md#loader-example)
143-
144-
### Generate standalone stylesheets
145-
146-
```ts
147-
import { writeFile } from 'node:fs/promises'
148-
import { css } from '@knighted/css'
149-
150-
// Build-time script that gathers all CSS imported by a React route
151-
const sheet = await css('./src/routes/marketing-page.tsx', {
152-
lightningcss: { minify: true, targets: { chrome: 120, safari: 17 } },
153-
})
154-
155-
await writeFile('./dist/marketing-page.css', sheet)
156-
```
157-
158-
### Inline CSS during SSR
159-
160-
```ts
161-
import { renderToString } from 'react-dom/server'
162-
import { css } from '@knighted/css'
163-
164-
export async function render(url: string) {
165-
const styles = await css('./src/routes/root.tsx')
166-
const html = renderToString(<App url={url} />)
167-
return `<!doctype html><style>${styles}</style>${html}`
168-
}
169-
```
170-
171-
### Custom resolver (enhanced-resolve example)
172-
173-
The built-in walker already leans on [`oxc-resolver`](https://github.com/oxc-project/oxc-resolver), so tsconfig `paths`, package `exports` conditions, and common extension aliases work out of the box. If you still need to mirror bespoke behavior (virtual modules, framework-specific loaders, etc.), plug in a custom resolver. Here’s how to use [`enhanced-resolve`](https://github.com/webpack/enhanced-resolve):
174-
175-
> [!TIP]
176-
> Hash-prefixed specifiers defined in `package.json#imports` resolve automaticallyno extra loader or `css()` options required. Reach for a custom resolver only when you need behavior beyond what `oxc-resolver` already mirrors.
177-
178-
> [!NOTE]
179-
> Sass-specific prefixes such as `pkg:#button` live outside Nodes resolver and still need a shim. See [docs/sass-import-aliases.md](./docs/sass-import-aliases.md) for a drop-in helper that strips those markers before `@knighted/css` walks the graph.
180-
181-
```ts
182-
import { ResolverFactory } from 'enhanced-resolve'
183-
import { css } from '@knighted/css'
184-
185-
const resolver = ResolverFactory.createResolver({
186-
extensions: ['.ts', '.tsx', '.js'],
187-
mainFiles: ['index'],
188-
})
189-
190-
async function resolveWithEnhanced(id: string, cwd: string): Promise<string | undefined> {
191-
return new Promise((resolve, reject) => {
192-
resolver.resolve({}, cwd, id, {}, (err, result) => {
193-
if (err) return reject(err)
194-
resolve(result ?? undefined)
195-
})
196-
})
197-
}
198-
199-
const styles = await css('./src/routes/page.tsx', {
200-
resolver: (specifier, { cwd }) => resolveWithEnhanced(specifier, cwd),
201-
})
202-
```
203-
204-
This keeps `@knighted/css` resolution in sync with your bundlers alias/extension rules.
205-
206-
### Specificity boost
207-
208-
Use `specificityBoost` to tweak selector behavior:
209-
210-
- **Strategies (built-in)**:
211-
- `repeat-class` duplicates the last class in matching selectors to raise specificity (useful when you need a real specificity bump).
212-
- `append-where` appends `:where(.token)` (zero specificity) for a harmless, order-based tie-breaker without changing matching.
213-
- **Custom visitor**: Supply your own Lightning CSS visitor via `specificityBoost.visitor` for full control.
214-
- **match filtering**: Provide `match: (string | RegExp)[]` to target selectors. Matches are ORd; if any entry matches, the strategy applies. If omitted/empty, all selectors are eligible.
215-
216-
Example:
217-
218-
```ts
219-
import { css } from '@knighted/css'
220-
221-
const styles = await css('./src/entry.ts', {
222-
lightningcss: { minify: true },
223-
specificityBoost: {
224-
match: ['.card', /^\.btn/], // OR match
225-
strategy: { type: 'repeat-class', times: 1 },
226-
},
227-
})
37+
console.log(sheet) // use during SSR, static builds, or to inline Shadow DOM styles
22838
```
22939

230-
If you omit `match`, the strategy applies to all selectors. Use `append-where` when you don’t want to change specificity; use `repeat-class` when you do.
231-
232-
> [!NOTE]
233-
> For the built-in strategies, the last class in a matching selector is the one that gets duplicated/appended. If you have multiple similar classes, tighten your `match` (string or RegExp) to target exactly the selector you want boosted.
234-
235-
> [!TIP]
236-
> See [docs/specificity-boost-visitor.md](./docs/specificity-boost-visitor.md) for a concrete visitor example.
40+
- Need runtime imports? See [docs/loader.md](./docs/loader.md).
41+
- Want strong selector types? Run `npx knighted-css-generate-types` and follow [docs/type-generation.md](./docs/type-generation.md).
42+
- Hash-prefixed or Sass-specific specifiers? Guidance lives in [docs/hash-import-fixture.md](./docs/hash-import-fixture.md) and [docs/sass-import-aliases.md](./docs/sass-import-aliases.md).
23743

238-
## Demo
44+
## Contributing & Support
23945

240-
Want to see everything wired together? Check the full demo app at [css-jsx-app](https://github.com/morganney/css-jsx-app).
46+
1. Install deps with `npm install`.
47+
2. Run `npm run build` to compile `@knighted/css`.
48+
3. Use `npm run test` for unit coverage and `npm run test:e2e` for the Playwright matrix.
24149

242-
> [!TIP]
243-
> This repo also includes a [playwright workspace](./packages/playwright/src/lit-react/lit-host.ts) which serves as an end-to-end demo.
50+
Issues and feature ideas are always welcome via [GitHub issues](https://github.com/knightedcodemonkey/css/issues).
24451

24552
## License
24653

0 commit comments

Comments
 (0)