Skip to content

Commit 55b8768

Browse files
Docs infra and consistency pass from site audit
Chrome: - Enable Expressive Code frames: copy button on every block, filename title bars from title= or a leading // path comment - Add pitfall callout kind and a collapsible deep-dive directive - Table of contents: left rail with active marker, indented normal-weight h3s Consistency and factual fixes: - Data: addToCart throws through markSafeError so the rendered message matches production behavior - Reads page: state that Solid Router's query() declares GET for you - One session shape: getSession() from Sessions; locals.userId everywhere - Forms: server module moved out of src/routes (a .ts there is an API route); note inline vs exported action forms - Debugging: EFFECT_RELAY_TEAR repair text matches the runtime message - Quick start / Boundaries / Components: the hold on navigation is Solid's, not Loading's; Quick start uses the updater setter form - App structure: three rendering modes; prerender snippet shows imports - Nested routes: typed paths for the sign-in link, sign-in route added - Setup: App snippet aligned with the other two (paths import, Loading) - Data fetching: query lifetime described from source, not 'on navigation' - Testing: unpinned install commands; template package.json is the record - Remove version-history language from concept pages - Middleware: document both exports of virtual:file-routes - Closing sections normalized to '## Next steps'; added to five pages with reference links moved inline - Link text normalized to page titles; concepts index reading order adds Stores and Rendering and SSR - Frontmatter: drop dead mainNavExclude and titleTemplate, drop use_cases / tags from hand-written pages, add descriptions to 14 pages WRITING.md: callout kinds and rules, Avoid/Prefer convention, first-use gloss, Recap section, Next steps and Common problems headings, frontmatter fields, no version pins, code-block filename convention. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 8c10a64 commit 55b8768

48 files changed

Lines changed: 384 additions & 242 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

WRITING.md

Lines changed: 109 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -53,24 +53,61 @@ See below for an example of what this would look like in raw Markdown.
5353
|April | 4 |
5454
```
5555

56-
#### Asides
56+
#### Callouts and deep dives
5757

58-
Asides serve as callout boxes in Solid's documentation.
59-
They represent a section of the document that is related to the content surrounding the aside, but not directly relevant to the page.
60-
They work well with explaining how Solid differs from other popular frameworks, referring to other points in the documentation, or serving as a tangential note.
61-
We ask that you use them sparingly.
58+
A page written as one unbroken column of prose and code reads as generated.
59+
Callouts change the register: they mark what the reader must not miss, what they can skip, and what will bite them.
60+
Aim for two or three per Learn page, placed where the content changes kind, not where a paragraph felt long.
6261

63-
1. To use an aside, you must first import the correct component.
64-
2. Use the following command in your terminal. (Remember to be in the cloned Solid repo directory.)
62+
Write a callout as a container directive with an optional title in brackets:
6563

66-
3. Once you have the Aside component imported, simply follow the below example for how to add one to your document.
64+
```
65+
:::pitfall[Title in the reader's words]
66+
The wrong version, what it does, and the fix.
67+
:::
68+
```
69+
70+
The available kinds, and what each one is for:
71+
72+
| Kind | Use it for |
73+
| ----------- | ---------------------------------------------------------------------------------------------------------------------- |
74+
| `pitfall` | A tempting approach that looks right and is not. Show the wrong code, name the consequence, quote the dev warning. |
75+
| `caution` | A constraint the reader must respect: an ordering rule, a limit, a case the runtime does not check. |
76+
| `danger` | Data loss or a security exposure. |
77+
| `note` | Context the paragraph needs but the flow does not: a difference from another framework, a scope statement. |
78+
| `tip` | A shortcut or a better default the reader may not know to look for. |
79+
| `advanced` | Content for integrators and library authors that an app developer can skip. |
80+
| `deep-dive` | Collapsed by default. How something works under the hood, or why a rule exists. Never the only place a rule is stated. |
81+
82+
Rules:
83+
84+
- A `pitfall` needs three things: the wrong code, the observed consequence, and the fix.
85+
Where a development diagnostic fires, quote it and link its code to the Debugging reactivity guide.
86+
- A `deep-dive` must be skippable.
87+
If the reader needs it to use the API, it belongs in the body.
88+
Keep headings out of it so the page outline stays flat.
89+
- Do not put a callout inside a callout, and do not stack two in a row.
90+
- Callout titles are plain text; inline code in the bracket is dropped by the parser.
91+
- Do not use a callout to restate the paragraph above it.
92+
93+
Tabs group alternatives the reader picks one of, such as package managers or validation libraries:
6794

6895
```
69-
:::note
70-
content here
96+
::::tab-group[validation-library]
97+
98+
:::tab[Valibot]
99+
...
100+
:::
101+
102+
:::tab[Zod]
103+
...
71104
:::
105+
106+
::::
72107
```
73108

109+
Tab groups that share a name select together across the site.
110+
74111
### Code examples
75112

76113
Solid's documentation contains example code, like you've seen here in this guide.
@@ -97,6 +134,11 @@ npm install
97134
Code examples are vital to providing users with quick tips on how to use Solid.
98135
Knowing when to use inline code versus code blocks can further the understanding of the readers and users alike.
99136

137+
When a block shows the contents of a file, name the file.
138+
A first-line comment such as `// src/router.ts` is lifted into the block's title bar, or set it on the fence with `title="src/router.ts"`.
139+
Fragments that are not a whole file get no filename.
140+
Every block has a copy button; do not add prose telling the reader to copy.
141+
100142
### Images
101143

102144
Good documentation takes advantage of images, such as screenshots and graphics, to expand upon the written content.
@@ -123,6 +165,24 @@ Now that we've laid out how we'd like your contribution to look, we'd like to ta
123165
Please note that, for editing purposes, **each sentence gets its own line**.
124166
Paragraphs should have two lines between them.
125167

168+
### Frontmatter
169+
170+
Every hand-written page carries three fields:
171+
172+
```yaml
173+
---
174+
title: "Sessions and auth"
175+
version: "2.0"
176+
description: "Build a signed cookie session on the request event, sign users in and out from server functions, and authorize every server entry point."
177+
---
178+
```
179+
180+
`description` is one sentence, written for the search result and the `llms.txt` index: what the reader will be able to do after the page, not what the page "covers".
181+
Do not add `titleTemplate`; the browser title is built from `title` and the site name.
182+
`use_cases` and `tags` are emitted by the reference generator and are not used on hand-written pages.
183+
184+
Do not pin package versions in prose or install commands; point at the template's `package.json` for the versions known to work together.
185+
126186
### Headings
127187

128188
Headings act as the primary means of document organization.
@@ -224,15 +284,47 @@ Do not open with a definition ("A signal is a reactive primitive that...").
224284
Definitions belong after the reader knows why they need one.
225285

226286
Where the page continues a sequence, say what the reader saw on the previous page and build on it.
227-
The Quick start counter, the cart on the Reactivity and Components pages, and the product page on the Async page are the recurring examples; reuse them before inventing a new domain.
287+
The recurring example across Learn is a storefront: the Quick start counter, then a product page, a cart, a checkout address form, an account area, and orders.
288+
Reuse those before inventing a new domain, and do not claim a page follows one example unless it does.
228289

229290
### Show what happens, then explain why
230291

231292
Each section that introduces a behavior shows the code, states what the reader observes when it runs, and then explains the mechanism.
232293
"Click `+` and the subtotal changes; nothing else is touched" before "JSX expressions are tracking scopes."
294+
Every example gets that observation sentence; code followed directly by mechanism reads as a reference page.
233295

234296
Where a tempting wrong approach exists, show it, show what it does (including the exact development warning where there is one), and show the fix.
235297
A section that only shows the right way leaves the reader unable to recognize the wrong way in their own code.
298+
Mark the pair so it can be scanned:
299+
300+
```tsx
301+
// Avoid: the effect copies a value it could derive
302+
createEffect(
303+
() => fullName(),
304+
(name) => setDisplayName(name)
305+
);
306+
307+
// Prefer: derive it where it is read
308+
const displayName = createMemo(() => fullName());
309+
```
310+
311+
Then one sentence of what the `Avoid` version does when run, and the bracketed diagnostic code if one fires, linked to its section in Debugging reactivity.
312+
A `:::pitfall` callout is the right container when the wrong version is a common habit rather than a one-off.
313+
314+
### Gloss a term the first time it appears
315+
316+
Solid has vocabulary a new reader has not met: held update, settled, owner, tracking scope, projection, boundary.
317+
The first time a page uses one, say what it means in a clause and link the page that explains it.
318+
"the write is held (kept back until the data it needs has arrived; see Async reactivity)".
319+
After the first use, the bare term is fine.
320+
One new term per section; a paragraph that introduces three is a paragraph the reader will not finish.
321+
322+
### Close with a recap
323+
324+
Before the next-steps section, list the rules the page taught as five to eight one-line bullets under `## Recap`.
325+
Each line is a rule the reader can apply, not a heading restated: "Read signals inside JSX, a memo, or an effect's compute function" rather than "Reactivity basics".
326+
The recap is what gets screenshotted and quoted; write it as if it were the only part of the page someone reads.
327+
A page with fewer than four rules to recap does not need one.
236328

237329
### Test every example
238330

@@ -250,18 +342,21 @@ Guides that choose between approaches need a section on what to weigh, not only
250342

251343
### End with the next page, not the reference
252344

253-
The last section of a Learn page tells the reader where to go next and why, in terms of what they are trying to do.
345+
The last section of a Learn page is `## Next steps`: two to four links, each with a clause saying what the reader gets there in terms of what they are trying to do.
346+
Use that heading on every page so readers and tooling can find it.
254347
Link API references inline where the API is discussed.
255348
Do not end a section or a page with "See the reference for details"; the reference is where the reader goes when they already understand the idea.
256349

350+
Troubleshooting goes under `## Common problems`, before Next steps, with each problem as an H3 in the reader's words so it appears in the page outline and can be linked.
351+
257352
### Vary the shape
258353

259354
Do not write every page as introduction, one H2 per API, conclusion.
260355
Let the problem decide the sections: a troubleshooting section named after the symptom, a three-pass build-up, a decision list, a comparison of two versions of the same code.
261356
Section headings should be readable as the reader's question or situation where that helps ("A value renders once and never updates"), not only as the name of the mechanism.
262357

263-
Do not add a closing paragraph that restates the page.
264-
If there is nothing to say after the last section but "where to go next", say only that.
358+
Do not add a closing paragraph that restates the page; the bulleted recap is the only summary.
359+
If there is nothing to say after the last section but "next steps", say only that.
265360

266361
### Use the reader's words for problems
267362

osmium/src/mdx-components.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,22 @@ import {
1919

2020
import { clientOnly } from "@solidjs/start";
2121
import { Callout } from "./ui/callout";
22+
import { DeepDive } from "./ui/deep-dive";
2223
import { Tabs, TabList, TabPanel, Tab } from "./ui/tabs";
2324

2425
export { Callout } from "./ui/callout";
26+
export { DeepDive } from "./ui/deep-dive";
2527
export { QuickLinks } from "./ui/quick-links";
2628
export { ImageLink } from "./ui/image-link";
2729

2830
const EraserLinkImpl = clientOnly(() => import("./ui/eraser-link"));
2931

30-
type CalloutType = "note" | "tip" | "advanced" | "caution" | "danger";
32+
type CalloutType =
33+
"note" | "tip" | "advanced" | "caution" | "danger" | "pitfall";
3134

3235
export const DirectiveContainer = (
3336
props: {
34-
type: "tab-group" | "tab" | CalloutType;
37+
type: "tab-group" | "tab" | "deep-dive" | CalloutType;
3538
title?: string;
3639
codeGroup?: string;
3740
tabNames?: string;
@@ -51,6 +54,9 @@ export const DirectiveContainer = (
5154
}
5255
>
5356
<Match when={props.type === "tab"}>{_children}</Match>
57+
<Match when={props.type === "deep-dive"}>
58+
<DeepDive title={props.title} children={_children} />
59+
</Match>
5460
<Match when={props.type === "tab-group"}>
5561
<TabGroup
5662
syncKey={props.title}

osmium/src/ui/callout.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
xCircle,
77
puzzlePiece,
88
bookOpen,
9+
noSymbol,
910
} from "solid-heroicons/solid";
1011

1112
const styles = {
@@ -34,6 +35,13 @@ const styles = {
3435
"bg-red-400/30 border-red-600 dark:border-red-400 dark:bg-red-400/20 dark:border-red-600",
3536
title: "text-red-900 dark:text-red-400",
3637
},
38+
// A tempting approach that looks right and is not. Distinct from
39+
// `caution` (a constraint to respect) and `danger` (data loss, security).
40+
pitfall: {
41+
container:
42+
"bg-orange-400/25 border-orange-600 dark:border-orange-400 dark:bg-orange-400/15 dark:border-orange-600",
43+
title: "text-orange-900 dark:text-orange-300",
44+
},
3745
};
3846

3947
const icons = {
@@ -72,6 +80,13 @@ const icons = {
7280
class={`${props.class} fill-red-500 dark:fill-red-400`}
7381
/>
7482
),
83+
pitfall: (props: { class?: string }) => (
84+
<Icon
85+
aria-hidden="true"
86+
path={noSymbol}
87+
class={`${props.class} fill-orange-600 dark:fill-orange-400`}
88+
/>
89+
),
7590
};
7691

7792
type CalloutType = keyof typeof styles;

osmium/src/ui/deep-dive.tsx

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { Icon } from "solid-heroicons";
2+
import { chevronRight, magnifyingGlassCircle } from "solid-heroicons/solid";
3+
import type { JSX } from "solid-js";
4+
5+
export type DeepDiveProps = {
6+
title?: string;
7+
children: JSX.Element;
8+
};
9+
10+
/**
11+
* Collapsed by default. Holds material most readers can skip: how something
12+
* works under the hood, why a rule exists, or an integrator-only detail.
13+
* Keep headings out of the body so the page outline stays flat.
14+
*/
15+
export function DeepDive(props: DeepDiveProps) {
16+
return (
17+
<details class="group my-6 w-full rounded-3xl border border-slate-400/60 bg-slate-100/60 open:bg-slate-100 dark:border-slate-600 dark:bg-slate-800/40 dark:open:bg-slate-800/60">
18+
<summary class="flex cursor-pointer list-none items-start gap-3 p-4 marker:content-none [&::-webkit-details-marker]:hidden">
19+
<Icon
20+
aria-hidden="true"
21+
path={magnifyingGlassCircle}
22+
class="mt-1 h-6 w-8 flex-none fill-slate-600 dark:fill-slate-300"
23+
/>
24+
<span class="flex w-full flex-col px-1">
25+
<span class="text-xs font-semibold tracking-wide text-slate-600 uppercase dark:text-slate-400">
26+
Deep dive
27+
</span>
28+
<span class="text-xl font-semibold text-slate-900 dark:text-white">
29+
{props.title ?? "How this works"}
30+
</span>
31+
</span>
32+
<Icon
33+
aria-hidden="true"
34+
path={chevronRight}
35+
class="mt-2 h-5 w-5 flex-none fill-slate-500 transition-transform group-open:rotate-90 dark:fill-slate-400"
36+
/>
37+
</summary>
38+
<div class="prose dark:prose-invert px-4 pb-4 pl-16 [&>*:first-child]:mt-0 [&>*:last-child]:mb-0">
39+
{props.children}
40+
</div>
41+
</details>
42+
);
43+
}

0 commit comments

Comments
 (0)