From f6668f5ec6150dc78ea30438dddc6630c49eb0c7 Mon Sep 17 00:00:00 2001 From: Essential Randomness Date: Tue, 19 May 2026 02:14:20 -0700 Subject: [PATCH 1/4] Improve style and flow of community@ --- astro.config.mjs | 7 +- package-lock.json | 69 ++- package.json | 4 +- .../docs/fujowebdev/style-guide/formatting.md | 141 ++++-- .../docs/fujowebdev/style-guide/structure.md | 143 ++++-- .../docs/fujowebdev/style-guide/style.md | 177 ++++--- src/index.css | 460 ++++++++++++++++-- src/plugins/remark-do-dont.ts | 152 ++++++ src/styles/do-dont.css | 179 +++++++ 9 files changed, 1124 insertions(+), 208 deletions(-) create mode 100644 src/plugins/remark-do-dont.ts create mode 100644 src/styles/do-dont.css diff --git a/astro.config.mjs b/astro.config.mjs index 25ec5ce..056b055 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -3,9 +3,14 @@ import { defineConfig } from "astro/config"; import starlight from "@astrojs/starlight"; import starlightSidebarTopics from "starlight-sidebar-topics"; import starlightLinksValidator from "starlight-links-validator"; +import remarkDirective from "remark-directive"; +import remarkDoDont from "./src/plugins/remark-do-dont.ts"; // https://astro.build/config export default defineConfig({ + markdown: { + remarkPlugins: [remarkDirective, remarkDoDont], + }, integrations: [ starlight({ title: "Community@", @@ -16,7 +21,7 @@ export default defineConfig({ href: "https://github.com/FujoWebDev/community-at", }, ], - customCss: ["./src/index.css"], + customCss: ["./src/index.css", "./src/styles/do-dont.css"], plugins: [ starlightSidebarTopics([ { diff --git a/package-lock.json b/package-lock.json index a26c321..b7eb2cd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,9 +10,11 @@ "dependencies": { "@astrojs/starlight": "^0.36.1", "astro": "^5.14.5", + "remark-directive": "^4.0.0", "sharp": "^0.32.5", "starlight-links-validator": "^0.19.2", - "starlight-sidebar-topics": "^0.6.2" + "starlight-sidebar-topics": "^0.6.2", + "unist-util-visit": "^5.1.0" } }, "node_modules/@astrojs/compiler": { @@ -105,7 +107,6 @@ "resolved": "https://registry.npmjs.org/@astrojs/starlight/-/starlight-0.36.1.tgz", "integrity": "sha512-Fmt8mIsAIZN18Y4YQDI6p521GsYGe4hYxh9jWmz0pHBXnS5J7Na3TSXNya4eyIymCcKkuiKFbs7b/knsdGVYPg==", "license": "MIT", - "peer": true, "dependencies": { "@astrojs/markdown-remark": "^6.3.1", "@astrojs/mdx": "^4.2.3", @@ -139,6 +140,41 @@ "astro": "^5.5.0" } }, + "node_modules/@astrojs/starlight/node_modules/micromark-extension-directive": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/micromark-extension-directive/-/micromark-extension-directive-3.0.2.tgz", + "integrity": "sha512-wjcXHgk+PPdmvR58Le9d7zQYWy+vKEU9Se44p2CrCDPiLr2FMyiT4Fyb5UFKFC66wGB3kPlgD7q3TnoqPS7SZA==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "parse-entities": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@astrojs/starlight/node_modules/remark-directive": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/remark-directive/-/remark-directive-3.0.1.tgz", + "integrity": "sha512-gwglrEQEZcZYgVyG1tQuA+h58EZfq5CSULw7J90AFuCTyib1thgHPoqQ+h9iFvU6R+vnZ5oNFQR5QKgGpk741A==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-directive": "^3.0.0", + "micromark-extension-directive": "^3.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/@astrojs/telemetry": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.3.0.tgz", @@ -1677,7 +1713,6 @@ "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -1840,7 +1875,6 @@ "resolved": "https://registry.npmjs.org/astro/-/astro-5.14.8.tgz", "integrity": "sha512-nKqCLs7BFvGQL9QWQOUqxHhlHtV0UMLXz1ANJygozvjcexBWS7FYkWI2LzRPMNYmbW4msIWNWnX2RvLdvI5Cnw==", "license": "MIT", - "peer": true, "dependencies": { "@astrojs/compiler": "^2.12.2", "@astrojs/internal-helpers": "0.7.4", @@ -4012,9 +4046,10 @@ } }, "node_modules/micromark-extension-directive": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/micromark-extension-directive/-/micromark-extension-directive-3.0.2.tgz", - "integrity": "sha512-wjcXHgk+PPdmvR58Le9d7zQYWy+vKEU9Se44p2CrCDPiLr2FMyiT4Fyb5UFKFC66wGB3kPlgD7q3TnoqPS7SZA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-directive/-/micromark-extension-directive-4.0.0.tgz", + "integrity": "sha512-/C2nqVmXXmiseSSuCdItCMho7ybwwop6RrrRPk0KbOHW21JKoCldC+8rFOaundDoRBUWBnJJcxeA/Kvi34WQXg==", + "license": "MIT", "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", @@ -4964,7 +4999,6 @@ "url": "https://github.com/sponsors/ai" } ], - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -5330,13 +5364,14 @@ } }, "node_modules/remark-directive": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/remark-directive/-/remark-directive-3.0.1.tgz", - "integrity": "sha512-gwglrEQEZcZYgVyG1tQuA+h58EZfq5CSULw7J90AFuCTyib1thgHPoqQ+h9iFvU6R+vnZ5oNFQR5QKgGpk741A==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/remark-directive/-/remark-directive-4.0.0.tgz", + "integrity": "sha512-7sxn4RfF1o3izevPV1DheyGDD6X4c9hrGpfdUpm7uC++dqrnJxIZVkk7CoKqcLm0VUMAuOol7Mno3m6g8cfMuA==", + "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-directive": "^3.0.0", - "micromark-extension-directive": "^3.0.0", + "micromark-extension-directive": "^4.0.0", "unified": "^11.0.0" }, "funding": { @@ -5499,7 +5534,6 @@ "version": "4.44.1", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.44.1.tgz", "integrity": "sha512-x8H8aPvD+xbl0Do8oez5f5o8eMS3trfCghc4HhLAnCkj7Vl0d1JWGs0UF/D886zLW2rOj2QymV/JcSSsw+XDNg==", - "peer": true, "dependencies": { "@types/estree": "1.0.8" }, @@ -6192,9 +6226,10 @@ } }, "node_modules/unist-util-visit": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", - "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz", + "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", + "license": "MIT", "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", @@ -6375,7 +6410,6 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", @@ -6570,7 +6604,6 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/package.json b/package.json index 62cb924..c76f5ad 100644 --- a/package.json +++ b/package.json @@ -12,9 +12,11 @@ "dependencies": { "@astrojs/starlight": "^0.36.1", "astro": "^5.14.5", + "remark-directive": "^4.0.0", "sharp": "^0.32.5", "starlight-links-validator": "^0.19.2", - "starlight-sidebar-topics": "^0.6.2" + "starlight-sidebar-topics": "^0.6.2", + "unist-util-visit": "^5.1.0" }, "repository": { "url": "https://github.com/FujoWebDev/community-at", diff --git a/src/content/docs/fujowebdev/style-guide/formatting.md b/src/content/docs/fujowebdev/style-guide/formatting.md index 7970a82..8301f22 100644 --- a/src/content/docs/fujowebdev/style-guide/formatting.md +++ b/src/content/docs/fujowebdev/style-guide/formatting.md @@ -6,63 +6,98 @@ sidebar: Our content is designed for *skimmability*: as you write it, imagine you’re only reading bits and pieces of it as you go in search of the sections most relevant to what you’re trying to do. -## **General Formatting** - -* Paragraphs: - * Keep them short and focused on a single idea. - * Be generous with them. - * The key idea should be in the first sentence of each paragraph. Follow up sentences provide additional context/information. - -* Bold: - * Use bold to highlight the key big idea in an article, or other crucial concepts. For highlights, try to capture a whole idea. For example: “As one of the most popular Version Control Systems, **Git helps you never lose track of changes to your code**, no matter how long it’s been since you last worked on it\!” - * How much of the sentence to highlight is a bit of an art\! As a general rule, highlight the one thing that you’d want the reader to take away from the article after they finish reading it. - * Make sure not to bold too often in a row, as it can get overwhelming and lose effectiveness. If you find yourself with multiple key big ideas, you should consider if they would be better served by being separated into their own article each. This also applies to sections and paragraphs, too. -* Italics: - * Group together longer titles without using “” (e.g. These articles are designed as companions to *FujoGuide Issue 1: Local Version Control with Git*, which you can buy…) - * Stress important points in sentences, e.g. pay *very close* attention to the path you give to the `rm` command. Similar to bold, you should not emphasize every other word in a sentence. If there are multiple important points in a single sentence, consider breaking them up into their unique paragraph to better space them out. - -* DFN - * Use DFN tag to highlight technical terms that aren’t code commands (e.g. In coding, it’s often useful to use **relative paths** for files and directories). - -* Linking: - * Use links generously, and make sure you’re linking descriptive text (not “`click here`” but “`find the official documentation here`”). - * You should link to: - * Relevant articles from elsewhere on `learn.fujoweb.dev`, especially for prerequisites, terms definitions, or deeper knowledge - * Authoritative external sources (e.g. `MDN`, official tool docs) - * If we don’t have a guide, we might try to find a good substitute on the web. However, these links tend to rot. - -* Lists: - * Use lists generously, as they help with skimmability. - * You should: - * Use numbered lists for items in sequence (e.g. we’ll learn 1\) how to do A 2\) how to do B…) - * Use bullet point lists for key takeaways or items not in a sequence (like this list\!) - * Use [Steps](https://starlight.astro.build/components/steps/) for sequences of instructions - -## **Code Formatting** - -* Inline Code: We use inline code to highlight: - * Commands (e.g. `git commit -m “your message”`) - * File names (`index.html`), paths `~/my_site/`, keyboard shortcuts (`ctrl + k`) or tool names (`vim`, `zsh`) -* Code blocks: Code blocks are an essential part of our guides, as they help people see how things work *in practice* and help break the flow of the text (and aid in skimmability). You should: - * Make sure to explain what the code does and why it’s there. - * Provide both input and output (unless confusing). You can omit part of the output if it helps the user focus on what you want to see. Similarly, you can make some output lighter to de-emphasize it. - * Use the code block captions to call out any particular element of the example you want the user to notice. - -## **Other Formatting** - -Images: - -* Use images to show non-code results, UIs, or examples of programs in action. You can annotate if needed, but be mindful that it makes them harder to edit later. -* If you cannot provide ALT text yourself, let us know: we’ll get someone to write it for you. +## General Formatting + +1. [Paragraphs](#1-paragraphs) +2. [Bold](#2-bold) +3. [Italics](#3-italics) +4. [Definition tag](#4-definition-tag) +5. [Linking](#5-linking) +6. [Lists](#6-lists) + +### 1. Paragraphs + +* Keep them short and focused on a single idea. +* Be generous with them. +* The key idea should be in the first sentence of each paragraph. Follow-up sentences provide additional context/information. + +### 2. Bold + +Use bold to highlight the key big idea in an article, or other crucial concepts. For highlights, try to capture a whole idea. For example: “As one of the most popular Version Control Systems, **Git helps you never lose track of changes to your code**, no matter how long it’s been since you last worked on it!” + +* How much of the sentence to highlight is a bit of an art! As a general rule, highlight the one thing that you’d want the reader to take away from the article after they finish reading it. +* Make sure not to bold too often in a row, as it can get overwhelming and lose effectiveness. If you find yourself with multiple key big ideas, you should consider if they would be better served by being separated into their own article each. This also applies to sections and paragraphs, too. + +### 3. Italics + +* Group together longer titles without using “” (e.g. These articles are designed as companions to *FujoGuide Issue 1: Local Version Control with Git*, which you can buy…) +* Stress important points in sentences, e.g. pay *very close* attention to the path you give to the `rm` command. Similar to bold, you should not emphasize every other word in a sentence. If there are multiple important points in a single sentence, consider breaking them up into their unique paragraph to better space them out. + +### 4. Definition tag + +Use the [definition tag (``)](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/dfn) to highlight technical terms that aren’t code commands (e.g. In coding, it’s often useful to use relative paths for files and directories). It semantically marks the term being defined in a sentence. + +### 5. Linking + +Use links generously, and make sure you’re linking descriptive text (not “`click here`” but “`find the official documentation here`”). + +You should link to: + +* Relevant articles from elsewhere on `learn.fujoweb.dev`, especially for prerequisites, terms definitions, or deeper knowledge +* Authoritative external sources (e.g. `MDN`, official tool docs) +* If we don’t have a guide, we might try to find a good substitute on the web. However, these links tend to rot. + +### 6. Lists + +Use lists generously, as they help with skimmability. You should: + +* Use numbered lists for items in sequence (e.g. we’ll learn 1. how to do A 2. how to do B…) +* Use bullet point lists for key takeaways or items not in a sequence (like this list!) +* Use [Steps](https://starlight.astro.build/components/steps/) for sequences of instructions + +## Code Formatting + +1. [Inline Code](#1-inline-code) +2. [Code blocks](#2-code-blocks) + +### 1. Inline Code + +We use inline code to highlight: + +* Commands (e.g. `git commit -m “your message”`) +* File names (`index.html`), paths `~/my_site/`, keyboard shortcuts (`ctrl + k`) or tool names (`vim`, `zsh`) + +### 2. Code blocks + +Code blocks are an essential part of our guides, as they help people see how things work *in practice* and help break the flow of the text (and aid in skimmability). You should: + +* Make sure to explain what the code does and why it’s there. +* Provide both input and output (unless confusing). You can omit part of the output if it helps the user focus on what you want to see. Similarly, you can make some output lighter to de-emphasize it. +* Use the code block captions to call out any particular element of the example you want the user to notice. + +## Other Formatting + +1. [Images](#1-images) +2. [Diagrams](#2-diagrams) +3. [Tabs](#3-tabs) + +### 1. Images + +* Use images to show non-code results, UIs, or examples of programs in action. You can annotate if needed, but be mindful that it makes them harder to edit later. +* If you cannot provide ALT text yourself, let us know: we’ll get someone to write it for you. * Use captions to help the reader understand what they’re looking at and what matters about it. -Diagrams: +### 2. Diagrams -* [We use Excalidraw for diagrams](https://excalidraw.com/). +* [We use Excalidraw for diagrams](https://excalidraw.com/). * Make sure you download the `.excalidraw` file and not just the PNG. -Tabs: +### 3. Tabs + +When a command differs across programs or Operating Systems, you can use [Tabs](https://starlight.astro.build/components/tabs/) to provide alternative versions of commands or explanations. + +:::tip[More components] -* When a command differs across programs or Operating Systems, you can use [Tabs](https://starlight.astro.build/components/tabs/) to provide alternative versions of commands or explanations. +We can easily use [all components from Starlight](https://starlight.astro.build/components/steps/). If you think a specific functionality not covered by these would help, you can ask Ms Boba whether it can be provided. -**More components:** we can easily use [all components from Starlight](https://starlight.astro.build/components/steps/). If you think a specific functionality not covered by these would help, you can ask Ms Boba whether it can be provided. +::: diff --git a/src/content/docs/fujowebdev/style-guide/structure.md b/src/content/docs/fujowebdev/style-guide/structure.md index 5bd8286..060fb09 100644 --- a/src/content/docs/fujowebdev/style-guide/structure.md +++ b/src/content/docs/fujowebdev/style-guide/structure.md @@ -1,85 +1,158 @@ --- -title: Articles Guidelines +title: Article Guidelines sidebar: order: 3 --- -## **Article Function** +## Article Function All articles should follow three key principles: -1. They answer a key question in the web development journey. +
    +
  1. Answer one key question
  2. +
  3. Less is more: be precise, concise, and factual
  4. +
  5. Every part of the article should add value
  6. +
+ +### a. Answer one key question For example: “What is NPM?” Or, “Why use JS?” -When you’re writing an article, always circle back to your main question, and ask yourself if what you’re writing is building on the answer or if it’s derailing into a separate topic. +When you’re writing an article, always circle back to your main question, and ask yourself if what you’re writing is building on the answer or if it’s derailing into a separate topic. A lot of web development topics are tightly interconnected: try to balance the need to position the information within the larger picture, and not overwhelming our audience by obfuscating the path ahead. -2. Less is more: you should be precise, concise and factual. +### b. Less is more: be precise, concise, and factual -For example: Instead of “*To correctly use arrays, we must give them a name. This name will then be referred to as a variable that can be referenced in the code.*” We prefer: “*To give a name to an array, we assign it to a variable.*” +For example: Instead of “_To correctly use arrays, we must give them a name. This name will then be referred to as a variable that can be referenced in the code._” We prefer: “_To give a name to an array, we assign it to a variable._” -When you’re writing an article, try to keep a simple sentence structure. Actions and consequences should be clearly defined and follow logically in the text. Prioritize actions over descriptions, clearly stating the “*who*”, “*what*”, “*where*” and “*when*”, for step-by-step guides. +When you’re writing an article, try to keep a simple sentence structure. Actions and consequences should be clearly defined and follow logically in the text. Prioritize actions over descriptions, clearly stating the “_who_”, “_what_”, “_where_” and “_when_”, for step-by-step guides. We expect our work to be used as reference material, which means our audience will come back to it when they need a reminder and will skim through to find the piece they’re missing. Make it easy by structuring things logically and avoid burying important information in large paragraphs. -3. Every part of the article should add value to the whole. +### c. Every part of the article should add value -Not only the words you use, but the formatting you choose should be looked at from this angle. Ask yourself, “*what is the value added by…?*” when it comes to adding sections or asides. +Not only the words you use, but the formatting you choose should be looked at from this angle. Ask yourself, “_what is the value added by…?_” when it comes to adding sections or asides. We must balance the casual tone of our overall communication with the volume of information we’re providing, and make sure we’re not burying our readers on either too much detail or too much chatter. In general, think of our articles as your response when a friend asks you a technical question: Yes, you want to make sure you don’t come across as pedantic or overwhelming, so you keep the friendly tone, but you should respect your friend and answer as clearly as you can. -## **Article Structure** +## Article Structure + +Every article is built from four parts: + +1. [Title](#1-title) +2. [Intro Section](#2-intro-section) +3. [Article Sections](#3-article-sections) +4. [Outro Section](#4-outro-section) + +### 1. Title + +It should be clear and descriptive. Think about the question the article is answering, and how to make it searchable and easily identifiable by the reader. + +### 2. Intro Section + +- _Quick introduction to the article topic:_ a couple of sentences that help the reader understand whether they’re interested in the subject at all. + +- _“After reading this article, you’ll know”:_ a list of bullet points explaining the major topics the article covers, each linking to the specific section where the topic is presented. These will often correspond to section titles, but can use different language if needed to be descriptive. + +- _Prerequisites:_ let the reader know if they need specific knowledge before tackling this article. If we have articles covering this knowledge, make sure to link to them. + +### 3. Article Sections + +- Use descriptive section titles. It’s ok to be a bit funny, if things remain clear in context (e.g. “`The nuclear exit option`”, “`Forcing TypeScript to Shut Up`”) + +- Use subsections generously. Think about where a confused friend could use a reference to a specific subsection! + +- Show practical examples whenever you can. For example, by showing a sample of commands and outputs that illustrate the topic in practice. See [Choosing commands and examples](#choosing-commands-and-examples) for how to pick them. + +- Use callout boxes/asides to flag information that doesn’t belong in the main flow. See [Callout boxes](#callout-boxes) for the full reference. + +### 4. Outro Section + +(Note: we don’t really have this now, but we should.) + +- _A short summary_ of what the reader can now do thanks to what they learned. + +- _Suggestions for next steps:_ could be other articles (“`Now that you know how to open a terminal, you can learn how to run programs or navigate your filesystem`”), or an invitation to try things out (“`Now try building your own NodeJS programs`”). + +- If relevant, a call to action to check out our paid offerings (like the Git zine). + +## Extras + +### Choosing commands and examples + +When you choose a command, make sure that its level is appropriate to the current level of the audience (e.g. they might not know what “changing a port” means). + +Good commands depend on the article and its positioning in the surrounding content ecosystem, but they generally tend to be basic, of broad usefulness, and easy to understand (e.g. `ls` in terminal, or `npm install`). + +Rule of thumb for choosing commands or examples: + +1. Do they already know it? Is this something they’re likely to want to do often? IF NOT, + +2. Do we expect them to learn about it soon? Would it be useful for them to learn this at this stage? IF NOT, + +3. Can we explain in simple words why something would be useful to them, even if they don’t have the full context or appreciation of the topic? + +### Callout boxes + +Use callout boxes/asides for information that supports the main flow without belonging in it. Each type has a specific purpose. + +A callout box can also include a collapsed section for further explanations that are not necessary and might be overwhelming to the casual reader. The callout box should still include a broad overview of the topic to help the reader determine whether they want to read further. The collapsed elements should have descriptive titles so readers know whether to open them! + +#### `:::note` + +Information that is helpful to know but that isn’t solving an immediate problem. Might be minor clarifications that enhance understanding without being critical, alternative approaches, or interesting background. + +_Examples include_: historical reasons for design choices, alternative tools, gentle reminders of past concepts. This should be short and help contextualize information, but not derail the article. -1. **Title:** It should be clear and descriptive. Think about the question the article is answering, and how to make it searchable and easily identifiable by the reader. +:::note[Does NPM really stand for Node Package Manager?] -2. **Intro Section** +Actually, no: [it was known as NPM before Node Package Manager](https://en.wikipedia.org/wiki/Npm), so it’s a backronym. -* *Quick introduction to the article topic:* a couple of sentences that help the reader understand whether they’re interested in the subject at all. +But it’s a good mnemonic, so we’ll continue spreading lies on purpose. -* *“After reading this article, you’ll know”:* a list of bullet points explaining the major topics the article covers, each linking to the specific section where the topic is presented. These will often correspond to section titles, but can use different language if needed to be descriptive. +::: -* *Prerequisites:* let the reader know if they need specific knowledge before tackling this article. If we have articles covering this knowledge, make sure to link to them. +#### `:::tip` -3. **Article Sections** +Best practices, clever shortcuts, _great_ advice, or links to related guides. These are positive suggestions that will help people work smarter, get over anxieties, or learn more about the topic. -* Use descriptive section titles. It’s ok to be a bit funny, if things remain clear in context (e.g. “`The nuclear exit option`”, “`Forcing TypeScript to Shut Up`”) +_Examples include_: common keyboard shortcuts, optional (but useful) features, “this is hard for experts too”, links to deeper explanations of related topics. -* Use subsections generously. Think about where a confused friend could use a reference to a specific subsection! +:::tip[Help! I can’t memorize the commands!] -* Show practical examples whenever you can. For example, by showing a sample of commands and outputs that illustrate the topic in practice. +Command-line programs often have documentation (books or websites) you can use as a reference for their commands. Most even have an internal help menu which provides a built-in way to jog your memory as needed. - * When you choose a command make sure that its level is appropriate to the current level of the audience (e.g. they might not know what “changing a port” means). +**Remember: no one remembers every command for every command-line program they use!** - * Good commands depend on the article and its positioning in the surrounding content ecosystem, but they generally tend to be basic, of broad usefulness, and easy to understand (e.g. `ls` in terminal, or `npm install`). +::: - * Rule of thumbs for choosing commands or examples: +#### `:::caution` - 1. Do they already know them? Is this something they’re likely to want to do often? IF NOT, +A common pitfall, error, or unexpected behavior that might lead to frustration (bugs, wasted time…) but not cause irrecoverable loss or security/privacy risks. - 2. Do we expect them to learn about it soon? Would it be useful for them to learn this at this stage? IF NOT +_Examples include_: this command requires absolute file paths, if this setting isn’t configured you might encounter an error. - 3. Can we explain in simple words why something would be useful to them, even if they don’t have the full context or appreciation of the topic? +:::caution[Error: Cannot find module!] -* **Use callout boxes/asides for:** +Seeing a scary `Error: Cannot find module […]test.js`? You may be in the wrong folder! - * **`::note`:** Information that is helpful to know but that isn’t solving an immediate problem. Might be minor clarifications that enhance understanding without being critical, alternative approaches, or interesting background. *Examples include*: historical reasons for design choices, alternative tools, gentle reminders of past concepts. This should be short and help contextualize information, but not derail the article. +You can use `ls` to see the files in the current directory: if `test.js` is not there, you may have taken a wrong turn! Use `pwd` to check which directory you’re in, and `cd [path]` to change directory. - * **`:::tip`:** Best practices, clever shortcuts, *great* advice, or links to related guides. These are positive suggestions that will help people work smarter, get over anxieties, or learn more about the topic. *Examples include*: common keyboard shortcuts, optional (but useful) features, “this is hard for experts too”, links to deeper explanations of related topics. +::: - * **`:::caution`:** A common pitfall, error, or unexpected behavior that might lead to frustration (bugs, wasted time…) but not cause irrecoverable loss or security/privacy risks. *Examples include*: this command requires absolute file paths, if this setting isn’t configured you might encounter an error. +#### `:::danger` - * **`:::danger`:** things that can go very wrong, either in unrecoverable ways, or with significant negative consequences\! *Examples include:* making sure readers know about the dangers of `rm -rf`, or things that might accidentally cause privacy leaks (e.g. `git` user settings). Make sure that if you call out a potential pitfall, you *also* call out the solution. Or at least *a* solution, or how to prevent it. Do not say “Don’t do X” and leave it at that. Instead try “Don’t do X, do Y instead.” +Things that can go very wrong, either in unrecoverable ways, or with significant negative consequences! Make sure that if you call out a potential pitfall, you _also_ call out the solution. Or at least _a_ solution, or how to prevent it. Do not say “Don’t do X” and leave it at that. Instead try “Don’t do X, do Y instead.” -A callout box can also include a collapsed section for further explanations that are not necessary and might be overwhelming to the casual reader. The callout box should still include a broad overview of the topic to help the reader determine whether they want to read further. The collapsed elements should have descriptive titles so readers know whether to open them\! +_Examples include:_ making sure readers know about the dangers of `rm -rf`, or things that might accidentally cause privacy leaks (e.g. `git` user settings). -4. **Outro Section** (note: we don’t really have this now, but we should) +:::danger[A dangerous exception: `rm -rf`] -* *A short summary* of what the reader can now do thanks to what they learned. +There is one command you should never run without checking the path carefully: the infamous (but very useful) `rm -rf [directory_path]`. This command erases ALL files inside `[directory_path]` without stopping to ask you for permission. It even skips the trash bin! -* *Suggestions for next steps:* could be other articles (“`Now that you know how to open a terminal, you can learn how to run programs or navigate your filesystem`”), or an invitation to try things out (“`Now try building your own NodeJS programs`”). +If you’re worried about accidentally deleting the wrong folder, you can use the normal file explorer instead of your terminal for the more dangerous file operations. -* If relevant, a call to action to check out our paid offerings (like the Git zine). +::: diff --git a/src/content/docs/fujowebdev/style-guide/style.md b/src/content/docs/fujowebdev/style-guide/style.md index e677611..3c3f100 100644 --- a/src/content/docs/fujowebdev/style-guide/style.md +++ b/src/content/docs/fujowebdev/style-guide/style.md @@ -8,96 +8,151 @@ This guide is here to create a consistent style our readers can come to trust an When writing for FujoGuide, **think of yourself as a knowledgeable friend**, rather than a formal instructor. You are deeply invested in the reader’s success, have a strong belief in their abilities, and yet relate to their present struggles—you too have been an *intimidated beginner* once\! -## **Tone and Voice** +## Tone and Voice Your tone should balance credibility and approachability by being: -**Practical & Instructional:** + + +### Practical & Instructional * Set clear expectations for the reader and stick to what you’ve outlined. * Keep steps simple: one action per step. * Action and consequences should be clearly labeled and follow logically. * Write with the expectation that your reader is following along with your instructions, so don’t lead them to a metaphorical cliff without warning clearly that the risk is there -| Do | Do not | -|-----|-----| -| | | - -**Approachable & Conversational:** +:::do +* Focus on the *hows* and the necessary *whys.* +* State prerequisites clearly and explain how to fulfill them. +* Highlight the value of what you’re sharing (“a good git config will save you future pain!”, “Using a terminal will unlock new useful tools!”). +* Give accurate explanations. +* Use consistent terminology. +* Provide clear steps and examples. +* Call out potential traps and warn appropriately before giving the instruction. +* Keep warnings and other callouts relevant: it’s okay to give a heads up for the next step, but a warning for seven steps ahead might get lost and not be as effective. +::: + +:::dont +* Downplay your expertise. +* Assume previous knowledge without stating your assumptions clearly. +* Be unnecessarily pedantic: it’s okay to omit corner cases when they don’t provide value. +* Give open-ended instructions that could lead them to a trap. +* Skip steps: assume your reader is doing what you’re telling them for the first time ever. +::: + +### Approachable & Conversational * Your reader should feel spoken to, not lectured at. * The occasional meme or inside joke to break the tension is welcome, as long as it doesn’t derail the flow of information. * Keep the wording simple and accessible. * Don't be afraid of teaching people technical jargon when appropriate, but make sure to define it clearly in approachable terms. -| Do | Do not | -|-----|-----| -||| +:::do +* Talk directly to the reader using you/your. +* Use contractions like you’ll, it’s, don’t. +* Use clear, direct language with simple words and sentence structures. +* Use accurate terminology, and introduce acronyms the first time you use them. +::: + +:::dont +* Sound condescending — be *very* careful with obviously, simply, just. Use them to encourage, not to devalue. +* Use academic or jargon terms that hobbyists won’t encounter in practice, unless appropriate for an aside. +* Force analogies that oversimplify concepts without adding real value. +* Come across as “talking down” to the audience. +::: -**Empathetic & Reassuring:** +### Empathetic & Reassuring * Remember, friends helping friends is the goal. * Our audience comes to us for help, trusting that we won’t judge them for struggling, and we’ll do our best to answer in a way they can understand. * Our goal is to empower our audience to use this knowledge in their own projects. -| Do | Do not | -|-----|-----| -| || +:::do +* Acknowledge difficulties (“this part can be tricky”, “it’s normal to struggle with this”). +* Give encouragement and support (“take a deep breath”, “you got this!”). +* Demystify beliefs about experts (“professionals are always looking up stuff”, “many programmers don’t know how to exit Vim”). +* Encourage mastery by showing what readers will be able to do with their new skills. +::: -**Genuine & Empowering:** +:::dont +* Omit potential roadblocks or downplay their impact. +* Point out problems without offering solutions or at least pointing to external sources to resolve it. +* Catastrophize: be clear and precise about the possible challenges or mistakes that readers can make, and offer specific actions to prevent them. +::: + +### Genuine & Empowering * There’s no single right way of doing things: what we offer is a specific option. * Our ulterior motive is to unlock coding as a path for self-expression, not to dictate what that self-expression should look like. * Avoid hierarchical speech: we’re all peers helping each other. * We do this because we *genuinely* love technology and the possibilities it unlocks, and want our friends to love it and use it too. -| Do | Do not | -|-----|-----| -||| +:::do +* Use light humor if it fits naturally. +* Use references that are timeless and don’t alienate outsiders. +* Help people dream about future possibilities their new knowledge will unlock. +* Draw on your own past experiences, fears, and frustrations to bring human warmth to the content. +::: + +:::dont +* Try too hard to be funny. +* Imply there’s a unique or uniform learning path. +* Imply there’s a single way to be an expert. +* Make the reader feel left out because they don’t get a reference. +::: -## **Language Conventions** +## Language Conventions The following conventions help us craft effective explanations that are easy to parse and absorb. These rules aren’t meant to be prescriptive, but are a starting point to craft sentences from, and a tool to review them with: -* **Language Standard:** - * Stick to standard American English. - * Online parlance is welcome when appropriate. - * Be mindful of slang that can be misconstrued as aggressive, negative or hurtful. - * Use inclusive, neutral language. - * Avoid making readers feel ostracized because they don’t understand the references or jokes. - * Hate speech has no place in our work. - * Do not make people or categories of people the butt of the joke. - -* **Language Clarity:** - * Prioritize direct, unambiguous writing. - * Use language well: - * Prefer an active voice to be more direct, clear, and engaging—unless it makes a simple explanation convoluted! - * Cut unnecessary words (“`in order to => to`”) - * Prefer strong verbs over nouns (“`perform an installation => install`”) - * Prefer positive framing when you’re not giving an explicit warning (“`Don’t do [x] unless [y] => if [y] do [x]`”) - * Use transition words to smooth connections between sentences and paragraphs. - * Get to the point quickly: - * When introducing a concept, start with a straightforward definition (“`Git is a program to manage changes to your files…`”, “`Absolute file paths specify the file location starting from the root…`, “`Commands are instructions you give to your Terminal…`”) - * When explaining how to achieve a goal, prefer stating the goal first (“`To install a new library, you can run npm add [x]`”, “`To know the directory you’re currently in, use the pwd command`”) - * Break down complex ideas in multiple sentences and paragraphs. If a sentence is getting long, consider: - * moving the subject or central point closer to the beginning - * reframing it as multiple sentences - * using bullet points to break it down into parts - -* **Economy of Language:** - * Make sure you cover the important parts without long-winded explanations. Respect readers’ time and attention span\! - * The main content should prioritize information that is useful (the reader will need it to achieve their goals) and timely (is relevant to what they’re *currently* learning) - * If an important explanation doesn’t quite fit, consider whether it can: - * be featured in a more appropriate place - * grouped with other useful explanations to create a larger topic - * Use asides to call out special cases, or to give more advanced explanations most readers can safely skip - -* **Purposeful Language:** - * **Be consistent:** Use the same terms throughout the article and match the rest of the site–let us know where we got this wrong ourselves\! - * **Define key terms:** Clearly define terms and concepts when you first introduce them. Use bold to highlight the core part of the explanation to aid skimmability. Make sure the bold part reads like a sentence\! - * **Spell out acronyms:** When you introduce an acronym, explain what it stands for. You can choose whether to use: - * **Term (acronym):** if the full term is common or it helps initial understanding (“`A Version Control System (VCS) helps people…`”) - * **Acronym (term):** if the acronym is frequently encountered in the wild (“`NPM (Node Package Manager) is a powerful tool that…`”) - * **Separate Sentences:** if the expansion requires more context or you want to elaborate on its meaning (“`HTML defines the structure of your websites. Its name stands for HyperText Markup Language, highlighting its ability to…`”) - * **Clarify confusion:** explicitly call out terms that are often mixed up (e.g. `terminal program` vs `script` vs `command`) and differences across systems (e.g. `path separators`). When the differences are not important, acknowledge that and, if needed, add further explanations in an aside. - * **Avoid filler words:** Remove words that do not add value or clarity to the sentence. If you can say it in five words, don’t say it in ten. +### Language Standard + +* Stick to standard American English. +* Online parlance is welcome when appropriate. +* Be mindful of slang that can be misconstrued as aggressive, negative or hurtful. +* Use inclusive, neutral language. +* Avoid making readers feel ostracized because they don’t understand the references or jokes. +* Hate speech has no place in our work. +* Do not make people or categories of people the butt of the joke. + +### Language Clarity + +* Prioritize direct, unambiguous writing. +* Use language well: + * Prefer an active voice to be more direct, clear, and engaging—unless it makes a simple explanation convoluted! + * Cut unnecessary words (“`in order to => to`”) + * Prefer strong verbs over nouns (“`perform an installation => install`”) + * Prefer positive framing when you’re not giving an explicit warning (“`Don’t do [x] unless [y] => if [y] do [x]`”) + * Use transition words to smooth connections between sentences and paragraphs. +* Get to the point quickly: + * When introducing a concept, start with a straightforward definition (“`Git is a program to manage changes to your files…`”, “`Absolute file paths specify the file location starting from the root…`, “`Commands are instructions you give to your Terminal…`”) + * When explaining how to achieve a goal, prefer stating the goal first (“`To install a new library, you can run npm add [x]`”, “`To know the directory you’re currently in, use the pwd command`”) +* Break down complex ideas in multiple sentences and paragraphs. If a sentence is getting long, consider: + * moving the subject or central point closer to the beginning + * reframing it as multiple sentences + * using bullet points to break it down into parts + +### Economy of Language + +* Make sure you cover the important parts without long-winded explanations. Respect readers’ time and attention span! +* The main content should prioritize information that is useful (the reader will need it to achieve their goals) and timely (is relevant to what they’re *currently* learning) +* If an important explanation doesn’t quite fit, consider whether it can: + * be featured in a more appropriate place + * grouped with other useful explanations to create a larger topic +* Use asides to call out special cases, or to give more advanced explanations most readers can safely skip + +### Purposeful Language + +* **Be consistent:** Use the same terms throughout the article and match the rest of the site–let us know where we got this wrong ourselves! +* **Define key terms:** Clearly define terms and concepts when you first introduce them. Use bold to highlight the core part of the explanation to aid skimmability. Make sure the bold part reads like a sentence! +* **Spell out acronyms:** When you introduce an acronym, explain what it stands for. You can choose whether to use: + * **Term (acronym):** if the full term is common or it helps initial understanding (“`A Version Control System (VCS) helps people…`”) + * **Acronym (term):** if the acronym is frequently encountered in the wild (“`NPM (Node Package Manager) is a powerful tool that…`”) + * **Separate Sentences:** if the expansion requires more context or you want to elaborate on its meaning (“`HTML defines the structure of your websites. Its name stands for HyperText Markup Language, highlighting its ability to…`”) +* **Clarify confusion:** explicitly call out terms that are often mixed up (e.g. `terminal program` vs `script` vs `command`) and differences across systems (e.g. `path separators`). When the differences are not important, acknowledge that and, if needed, add further explanations in an aside. +* **Avoid filler words:** Remove words that do not add value or clarity to the sentence. If you can say it in five words, don’t say it in ten. diff --git a/src/index.css b/src/index.css index 5ff7115..eecf583 100644 --- a/src/index.css +++ b/src/index.css @@ -1,50 +1,432 @@ -/* Dark mode colors. */ +@import url("https://use.typekit.net/exd6spk.css"); + :root { - --sl-color-accent-low: #241f39; - --sl-color-accent: #6b56b6; - --sl-color-accent-high: #c8c4e6; - --sl-color-white: #ffffff; - --sl-color-gray-1: #f0eaff; - --sl-color-gray-2: #c5bbe0; - --sl-color-gray-3: #937dc3; - --sl-color-gray-4: #5f4a8a; - --sl-color-gray-5: #402966; - --sl-color-gray-6: #2e1652; - --sl-color-black: #1b122d; -} -/* Light mode colors. */ -:root[data-theme='light'] { - --sl-color-accent-low: #d6d3ee; - --sl-color-accent: #6d58b8; - --sl-color-accent-high: #332b54; - --sl-color-white: #1b122d; - --sl-color-gray-1: #2e1652; - --sl-color-gray-2: #402966; - --sl-color-gray-3: #5f4a8a; - --sl-color-gray-4: #937dc3; - --sl-color-gray-5: #c5bbe0; - --sl-color-gray-6: #f0eaff; - --sl-color-gray-7: #f7f4ff; - --sl-color-black: #ffffff; + --sl-color-accent-low: #2e1b5a; + --sl-color-accent: #a571eb; + --sl-color-accent-high: #e0d0ff; + --sl-color-white: #f7f4ff; + --sl-color-gray-1: #ebe4ff; + --sl-color-gray-2: #cfc3ec; + --sl-color-gray-3: #a594d2; + --sl-color-gray-4: #7d6aac; + --sl-color-gray-5: #4a3878; + --sl-color-gray-6: #2e1652; + --sl-color-black: #1a1130; + + --brand-pop: #ff7eb8; + --brand-pop-soft: rgba(255, 126, 184, 0.18); + --brand-gradient: linear-gradient(135deg, #a571eb 0%, #ff7eb8 100%); + --brand-glow: + 0 14px 40px -10px rgba(255, 126, 184, 0.55), + 0 4px 14px -4px rgba(165, 113, 235, 0.65); + + --sl-color-bg-sidebar: #20153f; + --sl-color-bg-nav: #2a1b54; + --sl-color-hairline-shade: rgba(165, 113, 235, 0.18); +} + +:root[data-theme="light"] { + --sl-color-accent-low: #e3daff; + --sl-color-accent: #6d3fcf; + --sl-color-accent-high: #2b1762; + --sl-color-white: #1a1130; + --sl-color-gray-1: #2e1652; + --sl-color-gray-2: #402966; + --sl-color-gray-3: #5f4a8a; + --sl-color-gray-4: #8775b8; + --sl-color-gray-5: #b5a8d6; + --sl-color-gray-6: #ebe4ff; + --sl-color-gray-7: #f5f0ff; + --sl-color-black: #fbf9ff; + + --brand-pop: #c12d72; + --brand-pop-soft: rgba(193, 45, 114, 0.12); + --brand-gradient: linear-gradient(135deg, #6d3fcf 0%, #c12d72 100%); + --brand-glow: + 0 14px 40px -10px rgba(193, 45, 114, 0.35), + 0 4px 14px -4px rgba(109, 63, 207, 0.35); + + --sl-color-bg-sidebar: #f5f0ff; + --sl-color-bg-nav: #ece4ff; + --sl-color-hairline-shade: rgba(109, 63, 207, 0.14); } :root { - padding-inline: 1rem; - --sl-content-gap-y: 2rem; + padding-inline: 1rem; + --sl-content-gap-y: 1.5rem; + + --sl-font: "nunito", system-ui, sans-serif; + --font-display: "freude", "nunito", system-ui, sans-serif; + + --text-h1: clamp(2.25rem, 2.4vw + 1.6rem, 3.25rem); + --text-h2: clamp(1.6rem, 1.1vw + 1.25rem, 2.1rem); + --text-h3: 1.35rem; + --text-h4: 1.1rem; + + --type-stroke: var(--sl-color-white); + --type-sticker-shadow: 3px 2px 0 + color-mix(in oklch, var(--sl-color-accent) 75%, var(--sl-color-black)); } -.sl-markdown-content p { - max-width: 80ch; +:root[data-theme="light"] { + --type-sticker-shadow: 3px 2px 0 var(--brand-pop); +} + +body, +.sidebar-content, +.sl-markdown-content { + font-family: var(--sl-font); +} + +.sl-markdown-content h1, +.sl-markdown-content h2, +.sl-markdown-content h3, +.sl-markdown-content h4, +.sl-markdown-content h5, +.sl-markdown-content h6, +h1#_top, +h1[data-page-title], +.hero h1, +.hero .tagline { + font-family: var(--font-display); + font-weight: 400; + font-style: normal; + letter-spacing: 0.005em; + line-height: 1.1; +} + +.hero h1 { + font-size: clamp(2.75rem, 4vw + 1.5rem, 4.5rem); + color: var(--brand-pop); + text-shadow: + -1.5px -1.5px 0 var(--type-stroke), + 1.5px -1.5px 0 var(--type-stroke), + -1.5px 1.5px 0 var(--type-stroke), + 1.5px 1.5px 0 var(--type-stroke); + filter: drop-shadow(var(--type-sticker-shadow)); + margin-block-end: 0.35em; +} + +.hero .tagline { + font-size: clamp(1.25rem, 0.6vw + 1.1rem, 1.6rem); + line-height: 1.3; + color: var(--sl-color-white); +} + +h1#_top, +h1[data-page-title], +.sl-markdown-content > h1 { + font-size: var(--text-h1); + color: var(--brand-pop); + text-shadow: + -1px -1px 0 var(--type-stroke), + 1px -1px 0 var(--type-stroke), + -1px 1px 0 var(--type-stroke), + 1px 1px 0 var(--type-stroke); + filter: drop-shadow( + 2px 1.5px 0 color-mix(in oklch, var(--sl-color-accent) 60%, transparent) + ); +} + +.sl-markdown-content h2 { + font-size: var(--text-h2); + color: var(--sl-color-white); + margin-block-start: 2em; +} + +.sl-markdown-content h3 { + font-size: var(--text-h3); + color: var(--sl-color-accent-high); + letter-spacing: 0.012em; +} + +.sl-markdown-content h4 { + font-size: var(--text-h4); + color: var(--sl-color-gray-2); +} + +:root[data-theme="light"] .sl-markdown-content :is(h2, h3, h4), +:root[data-theme="light"] + .starlight-sidebar-topics + a:not(.starlight-sidebar-topics-current) { + color: color-mix(in oklch, #1a1130 70%, var(--sl-color-accent)); +} + +.sl-markdown-content p, +.sl-markdown-content li { + font-size: 1rem; + line-height: 1.65; +} + +.sidebar { + border-inline-end: 1px solid var(--sl-color-hairline-shade); +} + +::selection { + background: var(--brand-pop); + color: var(--sl-color-black); +} + +.sl-markdown-content em { + color: var(--brand-pop); + font-style: italic; +} + +.sl-markdown-content del, +.hero del { + color: var(--sl-color-gray-3); + text-decoration-color: var(--brand-pop); + text-decoration-thickness: 2px; + text-underline-offset: 1px; } + .hero { - padding-block: clamp(2.5rem, calc(1rem + 10vmin), 4rem) + padding-block: clamp(2.5rem, calc(1rem + 10vmin), 4rem); +} + +.hero .actions .action.primary, +.action.primary { + background: var(--brand-gradient); + border: 0; + color: #fff; + transition: transform 220ms cubic-bezier(0.2, 0.8, 0.2, 1); +} +.hero .actions .action.primary:hover, +.action.primary:hover { + transform: translateY(-2px); +} + +.sl-link-card, +.card { + transition: border-color 220ms; +} +.sl-link-card:hover, +.card:hover { + border-color: var(--brand-pop); } + +.sl-markdown-content p { + max-width: 68ch; +} + +@media (min-width: 72rem) { + .main-pane > main > .content-panel { + padding-inline-start: 0.25rem; + --sl-content-margin-inline: 0.5rem; + } +} + @media (min-width: 50rem) { - .hero { - grid-template-columns: 1fr 1fr; - img { - width: 100%; - height: auto; - } + .hero { + grid-template-columns: 1fr 1fr; + img { + width: 100%; + height: auto; } -} \ No newline at end of file + } +} + +.sidebar-content [aria-current="page"], +.sidebar-content [aria-current="page"]:hover, +.sidebar-content [aria-current="page"]:focus { + background-color: var(--brand-pop-soft); + color: var(--brand-pop); +} + +.sidebar-content a:not([aria-current="page"]):hover { + background-color: var(--brand-pop-soft); + color: var(--sl-color-white); +} + +.sl-markdown-content > h2 { + display: flex; + align-items: center; + gap: 0.6rem; +} +.sl-markdown-content > h2::before { + content: ""; + width: 0.55em; + height: 0.55em; + border-radius: 999px; + background: var(--brand-gradient); + flex-shrink: 0; + box-shadow: 0 0 18px -2px + color-mix(in oklch, var(--brand-pop) 60%, transparent); +} + +.sl-markdown-content p + :is(ul, ol) { + margin-top: 1rem; +} + +.sl-markdown-content ul > li::marker { + color: var(--brand-pop); +} +.sl-markdown-content ol > li::marker { + color: var(--brand-pop); + font-weight: 700; +} + +.sl-markdown-content :not(pre) > code { + background-color: var(--brand-pop-soft); + color: var(--brand-pop); + padding: 0.1em 0.45em; + border-radius: 5px; + font-weight: 600; + font-size: 0.9em; + font-variant-numeric: tabular-nums; + border: 0; +} + +.sl-markdown-content blockquote { + position: relative; + border: 0; + background: linear-gradient( + 135deg, + color-mix(in oklch, var(--sl-color-accent) 10%, transparent), + color-mix(in oklch, var(--brand-pop) 12%, transparent) + ); + border-radius: 14px; + padding: 1.5rem 1.75rem; + margin-block: 1.5rem; +} +.sl-markdown-content blockquote > :first-child { + margin-top: 0; +} +.sl-markdown-content blockquote > :last-child { + margin-bottom: 0; +} + +.sl-markdown-content a { + text-decoration: underline; + text-decoration-color: var(--brand-pop); + text-decoration-thickness: 1.5px; + text-underline-offset: 3px; + font-weight: 600; + transition: text-decoration-thickness 180ms cubic-bezier(0.2, 0.8, 0.2, 1); +} +.sl-markdown-content a:hover { + text-decoration-style: wavy; + text-decoration-thickness: 2px; + text-underline-offset: 4px; +} + +mobile-starlight-toc nav { + background-color: color-mix( + in oklch, + var(--sl-color-bg-nav) 92%, + var(--brand-pop) + ); + border-top: 0; +} +mobile-starlight-toc .toggle { + background-color: color-mix( + in oklch, + var(--sl-color-accent) 12%, + transparent + ); + border-color: color-mix(in oklch, var(--sl-color-accent) 50%, transparent); + color: var(--sl-color-white); + transition: + background-color 180ms ease, + border-color 180ms ease, + box-shadow 180ms ease; +} +mobile-starlight-toc .toggle:hover, +mobile-starlight-toc details[open] .toggle { + background-color: color-mix(in oklch, var(--brand-pop) 18%, transparent); + border-color: var(--brand-pop); + box-shadow: 0 6px 18px -10px var(--brand-pop); +} + +mobile-starlight-toc .caret { + color: var(--brand-pop); + transition: transform 220ms cubic-bezier(0.2, 0.8, 0.2, 1); +} +mobile-starlight-toc details[open] .caret { + transform: rotate(90deg); +} + +mobile-starlight-toc .display-current { + color: var(--brand-pop); + font-weight: 600; +} + +mobile-starlight-toc .dropdown { + background-color: color-mix( + in oklch, + var(--sl-color-bg-nav) 88%, + var(--brand-pop) + ); + border-color: color-mix(in oklch, var(--brand-pop) 30%, transparent); + box-shadow: + 0 24px 60px -24px color-mix(in oklch, var(--brand-pop) 50%, transparent), + 0 8px 20px -12px rgba(0, 0, 0, 0.4); +} + +mobile-starlight-toc .dropdown ul.isMobile > li > a { + transition: + background-color 160ms ease, + color 160ms ease; +} +mobile-starlight-toc .dropdown ul.isMobile > li > a:hover, +mobile-starlight-toc .dropdown ul.isMobile > li > a:focus-visible { + background-color: var(--brand-pop-soft); + color: var(--brand-pop); +} +mobile-starlight-toc .dropdown ul.isMobile > li > a[aria-current="true"], +mobile-starlight-toc .dropdown ul.isMobile > li > a[aria-current="true"]:hover { + background-color: color-mix(in oklch, var(--brand-pop) 18%, transparent); + color: var(--brand-pop); + font-weight: 600; +} +mobile-starlight-toc + .dropdown + ul.isMobile + > li + > a[aria-current="true"]::after { + background-color: var(--brand-pop); +} + +mobile-starlight-toc .dropdown ul.isMobile > li > a { + border-top-color: color-mix(in oklch, var(--brand-pop) 14%, transparent); +} + +:focus-visible { + outline: 2px solid var(--brand-pop); + outline-offset: 2px; + border-radius: 4px; +} + +@media (prefers-reduced-motion: reduce) { + .hero .actions .action.primary, + .action.primary, + .sl-link-card, + .card, + .sl-markdown-content a { + transition: none; + } +} + +.sl-markdown-content .jump-list { + margin-block: 0.5em 1.5em; +} + +.sl-markdown-content .jump-list li { + margin-block: 0.2em; +} + +.sl-markdown-content .jump-list a { + text-decoration: none; + color: var(--sl-color-gray-2); + border-bottom: 1px dotted var(--sl-color-gray-4); + padding-block-end: 1px; + transition: + color 150ms, + border-color 150ms; +} + +.sl-markdown-content .jump-list a:hover, +.sl-markdown-content .jump-list a:focus { + color: var(--brand-pop); + border-bottom-color: var(--brand-pop); +} diff --git a/src/plugins/remark-do-dont.ts b/src/plugins/remark-do-dont.ts new file mode 100644 index 0000000..3171325 --- /dev/null +++ b/src/plugins/remark-do-dont.ts @@ -0,0 +1,152 @@ +import GithubSlugger from "github-slugger"; +import { toString as nodeText } from "mdast-util-to-string"; +import type { Paragraph, Root } from "mdast"; +import type { ContainerDirective } from "mdast-util-directive"; +import type { Plugin } from "unified"; +import type {} from "mdast-util-to-hast"; + +type ColumnKind = "do" | "dont"; + +const COLUMN: Record = { + do: { className: "do-column", label: "Do" }, + dont: { className: "dont-column", label: "Do not" }, +}; + +const isColumnKind = (name: string): name is ColumnKind => + name === "do" || name === "dont"; + +const makeLabel = ({ text, id }: { text: string; id: string }): Paragraph => ({ + type: "paragraph", + data: { hName: "div", hProperties: { id, className: "do-dont-label" } }, + children: [ + { + type: "link", + url: `#${id}`, + title: null, + data: { hProperties: { className: "do-dont-label-anchor" } }, + children: [{ type: "text", value: text }], + }, + ], +}); + +const makeContainer = ({ + name, + className, + children, +}: { + name: string; + className: string; + children: ContainerDirective["children"]; +}): ContainerDirective => ({ + type: "containerDirective", + name, + attributes: {}, + children, + data: { hName: "div", hProperties: { className } }, +}); + +/** + * Mutates a `:::do` / `:::dont` directive into a labeled column div. + * `sectionSlug` is the slug of the nearest preceding heading, used to + * scope the anchor id (`#${slug}-do` / `#${slug}-dont`). + */ +const makeColumn = ({ + node, + kind, + sectionSlug, +}: { + node: ContainerDirective; + kind: ColumnKind; + sectionSlug: string; +}): void => { + const { className, label } = COLUMN[kind]; + const id = sectionSlug ? `${sectionSlug}-${kind}` : kind; + node.data = { ...node.data, hName: "div", hProperties: { className } }; + node.children = [makeLabel({ text: label, id }), ...node.children]; +}; + +/** + * Transforms `:::do` + `:::dont` container directives into paired columns. + * + * :::do :::do-dont + * - item :::do + * ::: - item + * :::dont ::: + * - item :::dont + * ::: - item + * ::: + * ::: + * + * Adjacent siblings get wrapped automatically; an explicit `:::do-dont` + * wrapper is also honored. Each column gets a labeled div with a + * self-anchor link whose id derives from the nearest preceding heading slug. + */ +const remarkDoDont: Plugin<[], Root> = () => { + return (tree) => { + const slugger = new GithubSlugger(); + let sectionSlug = ""; + + const children = tree.children; + for (let i = 0; i < children.length; i++) { + const child = children[i]; + + // Track the nearest preceding heading so column ids can hang off it. + if (child.type === "heading") { + sectionSlug = slugger.slug(nodeText(child)); + continue; + } + if (child.type !== "containerDirective") continue; + + switch (child.name) { + // Explicit `:::do-dont` wrapper: add properties, build inner columns. + case "do-dont": { + child.data = { + ...child.data, + hName: "div", + hProperties: { className: "do-dont" }, + }; + for (const inner of child.children) { + if ( + inner.type !== "containerDirective" || + !isColumnKind(inner.name) + ) { + continue; + } + makeColumn({ node: inner, kind: inner.name, sectionSlug }); + } + break; + } + + // Bare `:::do`: wrap with adjacent `:::dont` if present. + case "do": { + const next = children[i + 1]; + makeColumn({ node: child, kind: "do", sectionSlug }); + if (next?.type !== "containerDirective" || next.name !== "dont") { + break; + } + makeColumn({ node: next, kind: "dont", sectionSlug }); + // Remove its sibling (the "dont"), wrapping each other tight in the do-dont + // This means the next pass won't find the ":::dont" and won't reprocess it + children.splice( + i, + 2, + makeContainer({ + name: "do-dont", + className: "do-dont", + children: [child, next], + }), + ); + break; + } + + // Standalone `:::dont` (no preceding `:::do`): make column only, no wrapper. + case "dont": { + makeColumn({ node: child, kind: "dont", sectionSlug }); + break; + } + } + } + }; +}; + +export default remarkDoDont; diff --git a/src/styles/do-dont.css b/src/styles/do-dont.css new file mode 100644 index 0000000..bbf7d89 --- /dev/null +++ b/src/styles/do-dont.css @@ -0,0 +1,179 @@ +/* + * Do / Do-not paired columns. Authored in markdown via + * :::do + * - item + * ::: + * :::dont + * - item + * ::: + * The remark-do-dont plugin wraps adjacent pairs in a .do-dont grid. + */ +.do-dont { + display: table; + width: calc(100% + 2em); + margin-inline-start: -1em; + table-layout: fixed; + margin-block: 2em 3.5em; + border-top: 1px dotted var(--sl-color-hairline-shade); + border-bottom: 1px dotted var(--sl-color-hairline-shade); + background-color: color-mix(in oklch, var(--sl-color-accent) 5%, transparent); +} + +:root[data-theme="light"] .do-dont { + background-color: color-mix(in oklch, var(--sl-color-accent) 4%, transparent); +} + +.do-column, +.dont-column { + display: table-cell; + vertical-align: top; + padding-block: 1em; + position: relative; + isolation: isolate; +} + +.do-column::before, +.dont-column::before { + content: ""; + position: absolute; + inset-block: 0.15em; + border-radius: 0.6em; + z-index: -1; + pointer-events: none; + transition: background-color 250ms ease-out; +} + +.do-column::before { + inset-inline: 0.4em; +} + +.dont-column::before { + inset-inline-start: -0.4em; + inset-inline-end: 0.4em; +} + +.do-column:has(.do-dont-label:target)::before { + background-color: rgba(46, 204, 113, 0.16); +} + +.dont-column:has(.do-dont-label:target)::before { + background-color: rgba(255, 85, 119, 0.16); +} + +:root[data-theme="light"] .do-column:has(.do-dont-label:target)::before { + background-color: rgba(31, 157, 85, 0.14); +} + +:root[data-theme="light"] .dont-column:has(.do-dont-label:target)::before { + background-color: rgba(192, 51, 74, 0.12); +} + +.do-column { + padding-inline-start: 1.5em; + padding-inline-end: 1rem; +} + +.dont-column { + padding-inline-start: 1rem; + padding-inline-end: 1.5em; +} + +.do-dont-label { + width: max-content; + margin: 0 0 0.9em 0; + font-family: var(--font-display); + font-size: 1.45rem; + font-weight: 400; + line-height: 1.1; + letter-spacing: 0.01em; + scroll-margin-top: 6rem; +} + +.sl-markdown-content .do-dont-label-anchor { + display: inline-block; + text-decoration: none; + padding-inline-end: 1em; + padding-block-end: 4px; + border-bottom: 2px dotted currentColor; +} + +.sl-markdown-content .do-column .do-dont-label-anchor { + color: #2ecc71; +} + +.sl-markdown-content .dont-column .do-dont-label-anchor { + color: #ff5577; +} + +:root[data-theme="light"] + .sl-markdown-content + .do-column + .do-dont-label-anchor { + color: #1f9d55; +} + +:root[data-theme="light"] + .sl-markdown-content + .dont-column + .do-dont-label-anchor { + color: #c0334a; +} + +.sl-markdown-content .do-dont-label-anchor:hover, +.sl-markdown-content .do-dont-label-anchor:focus { + color: var(--brand-pop); +} + +.do-column ul, +.dont-column ul { + list-style: none; + padding-inline-start: 0; + margin-block: 0; +} + +.do-column li, +.dont-column li { + position: relative; + padding-inline-start: 1.6em; + margin-block: 0.4em; +} + +.do-column li::before, +.dont-column li::before { + position: absolute; + left: 0; + top: 0; + font-weight: 700; + line-height: inherit; +} + +.do-column li::before { + content: "✓"; + color: #2ecc71; +} + +.dont-column li::before { + content: "✗"; + color: #ff5577; +} + +:root[data-theme="light"] .do-column li::before { + color: #1f9d55; +} + +:root[data-theme="light"] .dont-column li::before { + color: #c0334a; +} + +/* Stack on narrow viewports. */ +@media (max-width: 40rem) { + .do-dont, + .do-column, + .dont-column { + display: block; + } + + .do-column { + margin-block-end: 1.5em; + } +} From fb0b65890b8ecaa5e41e0cee07aaf71571d2903c Mon Sep 17 00:00:00 2001 From: Essential Randomness Date: Tue, 19 May 2026 02:21:36 -0700 Subject: [PATCH 2/4] Fix validator --- src/plugins/remark-do-dont.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/plugins/remark-do-dont.ts b/src/plugins/remark-do-dont.ts index 3171325..c127148 100644 --- a/src/plugins/remark-do-dont.ts +++ b/src/plugins/remark-do-dont.ts @@ -1,6 +1,6 @@ import GithubSlugger from "github-slugger"; import { toString as nodeText } from "mdast-util-to-string"; -import type { Paragraph, Root } from "mdast"; +import type { Heading, Root } from "mdast"; import type { ContainerDirective } from "mdast-util-directive"; import type { Plugin } from "unified"; import type {} from "mdast-util-to-hast"; @@ -15,8 +15,12 @@ const COLUMN: Record = { const isColumnKind = (name: string): name is ColumnKind => name === "do" || name === "dont"; -const makeLabel = ({ text, id }: { text: string; id: string }): Paragraph => ({ - type: "paragraph", +// Typed as `heading` (not `paragraph`) so starlight-links-validator sees the +// id and doesn't flag the self-link as invalid. `hName: "div"` keeps the +// rendered HTML still a div. +const makeLabel = ({ text, id }: { text: string; id: string }): Heading => ({ + type: "heading", + depth: 6, data: { hName: "div", hProperties: { id, className: "do-dont-label" } }, children: [ { From ef1f25fa076079fbd3b28fbcdd97e01136f47436 Mon Sep 17 00:00:00 2001 From: Essential Randomness Date: Tue, 19 May 2026 06:28:25 -0700 Subject: [PATCH 3/4] starlight pls --- astro.config.mjs | 7 +++- src/plugins/base-links-integration.ts | 58 +++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 src/plugins/base-links-integration.ts diff --git a/astro.config.mjs b/astro.config.mjs index 056b055..37fda5d 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -5,13 +5,18 @@ import starlightSidebarTopics from "starlight-sidebar-topics"; import starlightLinksValidator from "starlight-links-validator"; import remarkDirective from "remark-directive"; import remarkDoDont from "./src/plugins/remark-do-dont.ts"; +import baseLinksIntegration from "./src/plugins/base-links-integration.ts"; + +const base = process.env.BASE_PATH; // https://astro.build/config export default defineConfig({ + base, markdown: { remarkPlugins: [remarkDirective, remarkDoDont], }, integrations: [ + ...(base ? [baseLinksIntegration(base)] : []), starlight({ title: "Community@", social: [ @@ -66,7 +71,7 @@ export default defineConfig({ ], }, ]), - starlightLinksValidator(), + ...(base ? [] : [starlightLinksValidator()]), ], }), ], diff --git a/src/plugins/base-links-integration.ts b/src/plugins/base-links-integration.ts new file mode 100644 index 0000000..b299746 --- /dev/null +++ b/src/plugins/base-links-integration.ts @@ -0,0 +1,58 @@ +import { readdir, readFile, writeFile } from "node:fs/promises"; +import { extname, join } from "node:path"; +import { fileURLToPath } from "node:url"; +import type { AstroIntegration } from "astro"; + +const htmlAttributes = /\b(href|src)=(")(\/(?!\/|#)[^"]*)"/g; + +const baseLinksIntegration = (base: string): AstroIntegration => { + const prefix = base.replace(/\/$/, ""); + + const withBase = (url: string) => { + if (!prefix || prefix === "/" || url === prefix || url.startsWith(prefix + "/")) { + return url; + } + + return prefix + url; + }; + + const rewriteHtmlFile = async (filePath: string) => { + const html = await readFile(filePath, "utf8"); + const rewritten = html.replace( + htmlAttributes, + (_match, attribute: string, quote: string, url: string) => + `${attribute}=${quote}${withBase(url)}${quote}`, + ); + + if (rewritten !== html) { + await writeFile(filePath, rewritten); + } + }; + + const rewriteHtmlDirectory = async (directory: string) => { + const entries = await readdir(directory, { withFileTypes: true }); + + await Promise.all( + entries.map(async (entry) => { + const path = join(directory, entry.name); + if (entry.isDirectory()) { + await rewriteHtmlDirectory(path); + } else if (entry.isFile() && extname(entry.name) === ".html") { + await rewriteHtmlFile(path); + } + }), + ); + }; + + return { + name: "base-links-integration", + hooks: { + "astro:build:done": async ({ dir }) => { + if (!prefix || prefix === "/") return; + await rewriteHtmlDirectory(fileURLToPath(dir)); + }, + }, + }; +}; + +export default baseLinksIntegration; From 665ae6a8988209f563fa120c4379d2d1d6e22653 Mon Sep 17 00:00:00 2001 From: Essential Randomness Date: Tue, 19 May 2026 06:44:31 -0700 Subject: [PATCH 4/4] css fixies --- src/index.css | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/index.css b/src/index.css index eecf583..df73b90 100644 --- a/src/index.css +++ b/src/index.css @@ -210,9 +210,8 @@ h1[data-page-title], } @media (min-width: 72rem) { - .main-pane > main > .content-panel { - padding-inline-start: 0.25rem; - --sl-content-margin-inline: 0.5rem; + .main-pane { + --sl-content-margin-inline: auto; } }