[READ-ONLY] Mirror of https://github.com/bombshell-dev/docs. bomb.sh/docs
0

Configure Feed

Select the types of activity you want to include in your feed.

feat(clack): display demos in webcontainer (#27)

* feat(clack): add webcontainer examples

* feat(clack): improve terminal styles

* feat(clack): webcontainer runs

* fix(demo): update spawn logic

* fix(demo): update props, styling

* fix(demo): remove wrapper

* style(demo): update styles

* Update src/content/docs/clack/guides/examples.mdx

Co-authored-by: Yan <61414485+yanthomasdev@users.noreply.github.com>
Signed-off-by: Nate Moore <natemoo-re@users.noreply.github.com>

* Update package.json

Co-authored-by: Yan <61414485+yanthomasdev@users.noreply.github.com>
Signed-off-by: Nate Moore <natemoo-re@users.noreply.github.com>

* Update package.json

Co-authored-by: Yan <61414485+yanthomasdev@users.noreply.github.com>
Signed-off-by: Nate Moore <natemoo-re@users.noreply.github.com>

* feat(demo): wrap examples in WebContainer

* chore: remove unused file

* fix: buttons

* chore: wrap all examples in webcontainer

* feat: update syntax theme

* chore: upgrade deps

* chore: upgrade deps

* fix: terminal size

* fix: remove wrapper for complex examples

---------

Signed-off-by: Nate Moore <natemoo-re@users.noreply.github.com>
Co-authored-by: Yan <61414485+yanthomasdev@users.noreply.github.com>

authored by

Nate Moore
Yan
and committed by
GitHub
(Jan 29, 2026, 8:40 PM EST) 8410f37c 362f5a65

+520 -158
+1
.gitignore
··· 2 2 dist/ 3 3 # generated types 4 4 .astro/ 5 + public/snapshot 5 6 6 7 # dependencies 7 8 node_modules/
+12
astro.config.mjs
··· 12 12 site: "https://bomb.sh/", 13 13 base: "/docs", 14 14 outDir: "./dist/docs/", 15 + server: { 16 + headers: { 17 + 'Cross-Origin-Embedder-Policy': 'require-corp', 18 + 'Cross-Origin-Opener-Policy': 'same-origin' 19 + } 20 + }, 15 21 integrations: [ 16 22 starlight({ 17 23 title: "Bombshell", ··· 26 32 Head: "./src/starlightOverrides/Head.astro", 27 33 }, 28 34 expressiveCode: { 35 + themes: ['github-dark-high-contrast', 'github-light-high-contrast'], 36 + styleOverrides: { 37 + twoSlash: { 38 + cursorColor: 'rgba(255 0 210)' 39 + } 40 + }, 29 41 plugins: [ecTwoSlash()], 30 42 }, 31 43 editLink: {
+13 -3
package.json
··· 5 5 "scripts": { 6 6 "dev": "astro dev", 7 7 "start": "astro dev", 8 + "prebuild": "npm run snapshot", 8 9 "build": "astro build", 9 10 "preview": "astro preview", 10 - "astro": "astro" 11 + "astro": "astro", 12 + "snapshot": "node --experimental-strip-types ./scripts/snapshot.ts" 11 13 }, 12 14 "dependencies": { 13 15 "@astrojs/starlight": "^0.37.1", 14 16 "@bomb.sh/args": "^0.3.1", 15 - "@clack/core": "^1.0.0-alpha.7", 16 - "@clack/prompts": "1.0.0-alpha.9", 17 + "@clack/core": "^1.0.0", 18 + "@clack/prompts": "1.0.0", 17 19 "@types/node": "^22.19.3", 18 20 "astro": "^5.16.6", 19 21 "expressive-code-twoslash": "^0.5.3", 22 + "@webcontainer/api": "^1.6.1", 23 + "@webcontainer/snapshot": "^0.1.0", 24 + "@xterm/addon-fit": "^0.11.0", 25 + "@xterm/addon-web-links": "^0.12.0", 26 + "@xterm/xterm": "^6.0.0", 20 27 "sharp": "^0.33.5", 21 28 "starlight-sidebar-topics": "^0.6.2" 29 + }, 30 + "devDependencies": { 31 + "tinyexec": "^1.0.2" 22 32 }, 23 33 "pnpm": { 24 34 "onlyBuiltDependencies": [
+64 -11
pnpm-lock.yaml
··· 15 15 specifier: ^0.3.1 16 16 version: 0.3.1 17 17 '@clack/core': 18 - specifier: ^1.0.0-alpha.7 19 - version: 1.0.0-alpha.7 18 + specifier: ^1.0.0 19 + version: 1.0.0 20 20 '@clack/prompts': 21 - specifier: 1.0.0-alpha.9 22 - version: 1.0.0-alpha.9 21 + specifier: 1.0.0 22 + version: 1.0.0 23 23 '@types/node': 24 24 specifier: ^22.19.3 25 25 version: 22.19.3 26 + '@webcontainer/api': 27 + specifier: ^1.6.1 28 + version: 1.6.1 29 + '@webcontainer/snapshot': 30 + specifier: ^0.1.0 31 + version: 0.1.0 32 + '@xterm/addon-fit': 33 + specifier: ^0.11.0 34 + version: 0.11.0 35 + '@xterm/addon-web-links': 36 + specifier: ^0.12.0 37 + version: 0.12.0 38 + '@xterm/xterm': 39 + specifier: ^6.0.0 40 + version: 6.0.0 26 41 astro: 27 42 specifier: ^5.16.6 28 43 version: 5.16.6(@types/node@22.19.3)(rollup@4.55.1)(typescript@5.8.2) ··· 35 50 starlight-sidebar-topics: 36 51 specifier: ^0.6.2 37 52 version: 0.6.2(@astrojs/starlight@0.37.1(astro@5.16.6(@types/node@22.19.3)(rollup@4.55.1)(typescript@5.8.2))) 53 + devDependencies: 54 + tinyexec: 55 + specifier: ^1.0.2 56 + version: 1.0.2 38 57 39 58 packages: 40 59 ··· 97 116 resolution: {integrity: sha512-8XqW8xGn++Eqqbz3e9wKuK7mxryeRjs4LOHLxbh2lwKeSbuNR4NFifDZT4KzvjU6HMOPbiNTsWpniK5EJfTWkg==} 98 117 engines: {node: '>=18'} 99 118 100 - '@clack/core@1.0.0-alpha.7': 101 - resolution: {integrity: sha512-3vdh6Ar09D14rVxJZIm3VQJkU+ZOKKT5I5cC0cOVazy70CNyYYjiwRj9unwalhESndgxx6bGc/m6Hhs4EKF5XQ==} 119 + '@clack/core@1.0.0': 120 + resolution: {integrity: sha512-Orf9Ltr5NeiEuVJS8Rk2XTw3IxNC2Bic3ash7GgYeA8LJ/zmSNpSQ/m5UAhe03lA6KFgklzZ5KTHs4OAMA/SAQ==} 102 121 103 - '@clack/prompts@1.0.0-alpha.9': 104 - resolution: {integrity: sha512-sKs0UjiHFWvry4SiRfBi5Qnj0C/6AYx8aKkFPZQSuUZXgAram25ZDmhQmP7vj1aFyLpfHWtLQjWvOvcat0TOLg==} 122 + '@clack/prompts@1.0.0': 123 + resolution: {integrity: sha512-rWPXg9UaCFqErJVQ+MecOaWsozjaxol4yjnmYcGNipAWzdaWa2x+VJmKfGq7L0APwBohQOYdHC+9RO4qRXej+A==} 105 124 106 125 '@ctrl/tinycolor@4.2.0': 107 126 resolution: {integrity: sha512-kzyuwOAQnXJNLS9PSyrk0CWk35nWJW/zl/6KvnTBMFK65gm7U1/Z5BqjxeapjZCIhQcM/DsrEmcbRwDyXyXK4A==} ··· 526 545 '@mdx-js/mdx@3.1.1': 527 546 resolution: {integrity: sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==} 528 547 548 + '@msgpack/msgpack@3.1.3': 549 + resolution: {integrity: sha512-47XIizs9XZXvuJgoaJUIE2lFoID8ugvc0jzSHP+Ptfk8nTbnR8g788wv48N03Kx0UkAv559HWRQ3yzOgzlRNUA==} 550 + engines: {node: '>= 18'} 551 + 529 552 '@oslojs/encoding@1.1.0': 530 553 resolution: {integrity: sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==} 531 554 ··· 772 795 773 796 '@ungap/structured-clone@1.3.0': 774 797 resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} 798 + 799 + '@webcontainer/api@1.6.1': 800 + resolution: {integrity: sha512-2RS2KiIw32BY1Icf6M1DvqSmcon9XICZCDgS29QJb2NmF12ZY2V5Ia+949hMKB3Wno+P/Y8W+sPP59PZeXSELg==} 801 + 802 + '@webcontainer/snapshot@0.1.0': 803 + resolution: {integrity: sha512-PTIGQ3osUpTbK/dqB8RYbcZGv8IK+DJACx709z5sFbeIlngB3hUFpTEYFYs1SbUvr/AEQqvd0/bhc4ectrPRRw==} 804 + engines: {node: '>=16'} 805 + 806 + '@xterm/addon-fit@0.11.0': 807 + resolution: {integrity: sha512-jYcgT6xtVYhnhgxh3QgYDnnNMYTcf8ElbxxFzX0IZo+vabQqSPAjC3c1wJrKB5E19VwQei89QCiZZP86DCPF7g==} 808 + 809 + '@xterm/addon-web-links@0.12.0': 810 + resolution: {integrity: sha512-4Smom3RPyVp7ZMYOYDoC/9eGJJJqYhnPLGGqJ6wOBfB8VxPViJNSKdgRYb8NpaM6YSelEKbA2SStD7lGyqaobw==} 811 + 812 + '@xterm/xterm@6.0.0': 813 + resolution: {integrity: sha512-TQwDdQGtwwDt+2cgKDLn0IRaSxYu1tSUjgKarSDkUM0ZNiSRXFpjxEsvc/Zgc5kq5omJ+V0a8/kIM2WD3sMOYg==} 775 814 776 815 acorn-jsx@5.3.2: 777 816 resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} ··· 2154 2193 dependencies: 2155 2194 fontkit: 2.0.4 2156 2195 2157 - '@clack/core@1.0.0-alpha.7': 2196 + '@clack/core@1.0.0': 2158 2197 dependencies: 2159 2198 picocolors: 1.1.1 2160 2199 sisteransi: 1.0.5 2161 2200 2162 - '@clack/prompts@1.0.0-alpha.9': 2201 + '@clack/prompts@1.0.0': 2163 2202 dependencies: 2164 - '@clack/core': 1.0.0-alpha.7 2203 + '@clack/core': 1.0.0 2165 2204 picocolors: 1.1.1 2166 2205 sisteransi: 1.0.5 2167 2206 ··· 2479 2518 transitivePeerDependencies: 2480 2519 - supports-color 2481 2520 2521 + '@msgpack/msgpack@3.1.3': {} 2522 + 2482 2523 '@oslojs/encoding@1.1.0': {} 2483 2524 2484 2525 '@pagefind/darwin-arm64@1.4.0': ··· 2675 2716 - supports-color 2676 2717 2677 2718 '@ungap/structured-clone@1.3.0': {} 2719 + 2720 + '@webcontainer/api@1.6.1': {} 2721 + 2722 + '@webcontainer/snapshot@0.1.0': 2723 + dependencies: 2724 + '@msgpack/msgpack': 3.1.3 2725 + 2726 + '@xterm/addon-fit@0.11.0': {} 2727 + 2728 + '@xterm/addon-web-links@0.12.0': {} 2729 + 2730 + '@xterm/xterm@6.0.0': {} 2678 2731 2679 2732 acorn-jsx@5.3.2(acorn@8.15.0): 2680 2733 dependencies:
+45
scripts/snapshot.ts
··· 1 + import fs from 'node:fs/promises'; 2 + import { fileURLToPath } from 'node:url'; 3 + import { snapshot } from '@webcontainer/snapshot'; 4 + import {x} from 'tinyexec'; 5 + import { createHash } from "node:crypto"; 6 + 7 + const PACKAGE_JSON = { 8 + name: 'example', 9 + type: 'module', 10 + version: '0.0.0', 11 + dependencies: { 12 + "@bomb.sh/args": "latest", 13 + "@clack/core": "1.0.0-alpha.0", 14 + "@clack/prompts": "1.0.0-alpha.0" 15 + } 16 + } 17 + const IGNORE_FILES = ['*.md', '*.d.*', '*.map', 'LICENSE', 'license']; 18 + const rootDir = new URL('../', import.meta.url); 19 + const snapshotDir = new URL(`./snapshot-${hash()}/`, rootDir); 20 + const outFile = new URL('./public/snapshot', rootDir); 21 + 22 + async function run() { 23 + await fs.mkdir(snapshotDir, { recursive: true }); 24 + await fs.writeFile(new URL('package.json', snapshotDir), JSON.stringify(PACKAGE_JSON)); 25 + await x('npm', ['install'], { 26 + nodeOptions: { 27 + cwd: fileURLToPath(snapshotDir), 28 + } 29 + }) 30 + for await (const file of fs.glob(IGNORE_FILES.map(file => `**/${file}`), { cwd: fileURLToPath(snapshotDir) })) { 31 + await fs.rm(new URL(file, snapshotDir)); 32 + } 33 + const output = await snapshot(fileURLToPath(snapshotDir)); 34 + await fs.writeFile(outFile, output); 35 + await fs.rm(snapshotDir, { recursive: true, force: true }); 36 + console.log('snapshot generated'); 37 + } 38 + 39 + run(); 40 + 41 + function hash() { 42 + return createHash("shake256", { outputLength: 8 }) 43 + .update(Date.now().toString()) 44 + .digest("hex"); 45 + }
+76
src/components/WebContainer/Terminal.astro
··· 1 + <docs-terminal class="not-content" /> 2 + 3 + <style> 4 + docs-terminal { 5 + display: block; 6 + min-height: 256px; 7 + } 8 + </style> 9 + 10 + <script> 11 + import type { Terminal } from "@xterm/xterm"; 12 + import { theme } from "./theme.ts"; 13 + 14 + customElements.define( 15 + "docs-terminal", 16 + class extends HTMLElement { 17 + instance: Terminal | undefined; 18 + ro: ResizeObserver | undefined; 19 + updateSize = () => {}; 20 + 21 + connectedCallback() { 22 + this.boot(); 23 + } 24 + disconnectedCallback() { 25 + this.instance?.dispose(); 26 + this.ro?.disconnect(); 27 + } 28 + handleResize() { 29 + this.ro = new ResizeObserver(() => this.updateSize()); 30 + this.ro.observe(this); 31 + } 32 + async boot() { 33 + if (this.instance) { 34 + this.instance.options.theme = theme; 35 + this.instance.open(this); 36 + return; 37 + } 38 + this.innerHTML = ""; 39 + const [{ Terminal }, { FitAddon }, { WebLinksAddon }] = 40 + await Promise.all([ 41 + import("@xterm/xterm"), 42 + import("@xterm/addon-fit"), 43 + import("@xterm/addon-web-links"), 44 + ]); 45 + this.instance = new Terminal({ 46 + convertEol: true, 47 + cursorBlink: false, 48 + disableStdin: false, 49 + theme, 50 + fontSize: 16, 51 + fontFamily: "Menlo, courier-new, courier, monospace", 52 + }); 53 + 54 + this.instance.open(this); 55 + 56 + const fit = new FitAddon(); 57 + this.instance.loadAddon(fit); 58 + this.instance.loadAddon(new WebLinksAddon()); 59 + this.updateSize = () => fit.fit(); 60 + this.updateSize(); 61 + this.handleResize(); 62 + } 63 + } 64 + ); 65 + </script> 66 + 67 + <style is:global> 68 + @import "@xterm/xterm/css/xterm.css" layer(xterm); 69 + 70 + .xterm { 71 + padding: 24px 12px; 72 + } 73 + .xterm-viewport { 74 + background: #191B23; 75 + } 76 + </style>
+89
src/components/WebContainer/WebContainer.astro
··· 1 + --- 2 + import Terminal from "./Terminal.astro"; 3 + 4 + interface Props { 5 + name: string; 6 + } 7 + const { name } = Astro.props; 8 + --- 9 + 10 + <web-container name={name}> 11 + <slot /> 12 + <Terminal /> 13 + </web-container> 14 + 15 + <script> 16 + import type { Terminal } from "@xterm/xterm"; 17 + import { WebContainer } from "@webcontainer/api"; 18 + const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); 19 + let host: WebContainer; 20 + 21 + host = await WebContainer.boot({ workdirName: 'demo' }); 22 + const snapshotResponse = await fetch(`/docs/snapshot`); 23 + const snapshot = await snapshotResponse.arrayBuffer(); 24 + await host.mount(snapshot, { mountPoint: "/" }); 25 + 26 + customElements.define( 27 + "web-container", 28 + class extends HTMLElement { 29 + get dir() { 30 + return `${this.name}/` 31 + } 32 + get file() { 33 + return `${this.dir}index.js`; 34 + } 35 + get fileContent() { 36 + const text = this.querySelector("pre code")!.textContent; 37 + return `import { intro, outro } from "@clack/prompts";console.clear();intro("\\x1b[46m\\x1b[30m ${this.name} \\x1b[0m");\n${text};`; 38 + } 39 + get name() { 40 + return this.getAttribute("name")!; 41 + } 42 + get terminal(): Terminal | undefined { 43 + return (this.querySelector("docs-terminal") as any)?.instance; 44 + } 45 + async connectedCallback() { 46 + await host.fs.mkdir(this.dir, { recursive: true }); 47 + await host.fs.writeFile( 48 + this.file, 49 + this.fileContent, 50 + { encoding: "utf-8" } 51 + ); 52 + await this.render(); 53 + } 54 + 55 + async render() { 56 + const { terminal, name } = this; 57 + terminal?.reset(); 58 + const main = async () => { 59 + // we set an infinite loop so that when the node process exits, we restart 60 + while (true) { 61 + const process = await host.spawn("node", ["index.js"], { 62 + cwd: name, 63 + terminal: { rows: terminal?.rows!, cols: terminal?.cols! }, 64 + }); 65 + process.output.pipeTo( 66 + new WritableStream({ 67 + write(data) { 68 + terminal?.write(data); 69 + } 70 + }) 71 + ); 72 + // write the terminal input to the process 73 + const terminalWriter = terminal?.onData((data) => { 74 + const writer = process.input.getWriter(); 75 + writer.write(data); 76 + writer.releaseLock(); 77 + }); 78 + // wait for the process to finish 79 + await process.exit; 80 + await sleep(2000); 81 + terminal?.clear(); 82 + terminalWriter?.dispose(); 83 + } 84 + } 85 + main(); 86 + } 87 + } 88 + ); 89 + </script>
+30
src/components/WebContainer/theme.ts
··· 1 + export const theme = { 2 + cursor: "#C4C7CF", 3 + cursorAccent: '#00000000', 4 + foreground: "#FFFFFF", 5 + background: "#191B23", 6 + red: "#ff3e47", 7 + green: "#07F53F", 8 + yellow: "#FFE228", 9 + blue: "#054bff", 10 + magenta: "#ff00d2", 11 + cyan: "#00E5FF", 12 + white: "#FFFFFF", 13 + brightBlack: "#484A54", 14 + brightRed: "#FF3E47", 15 + brightGreen: "#07F53F", 16 + brightYellow: "#FFE228", 17 + brightBlue: "#054bff", 18 + brightMagenta: "#FF00D2", 19 + brightCyan: "#00E5FF", 20 + brightWhite: "#FFFFFF", 21 + selectionBackground: "#97979b33", 22 + }; 23 + 24 + 25 + 26 + 27 + 28 + 29 + 30 +
+27 -2
src/content/docs/clack/guides/examples.mdx
··· 3 3 description: Learn through practical examples of using Clack 4 4 --- 5 5 6 + import WebContainer from '/src/components/WebContainer/WebContainer.astro'; 7 + 6 8 This guide provides practical examples of using Clack in different scenarios. 7 9 8 10 ## Basic Examples 9 11 12 + ### Container 13 + 14 + <WebContainer name="container"> 15 + ```ts 16 + import { text, isCancel } from '@clack/prompts'; 17 + 18 + const name = await text({ 19 + message: "What is your name?", 20 + placeholder: "Jane Doe" 21 + }) 22 + ``` 23 + </WebContainer> 24 + 10 25 ### Simple Text Input 11 26 27 + <WebContainer name="simple-text"> 12 28 ```ts twoslash 13 29 import { text, isCancel } from '@clack/prompts'; 14 30 ··· 26 42 27 43 console.log(`Hello, ${name}!`); 28 44 ``` 45 + </WebContainer> 29 46 30 47 ### Selection Menu 31 48 49 + <WebContainer name="selection-menu"> 32 50 ```ts twoslash 33 51 import { select, isCancel } from '@clack/prompts'; 34 52 ··· 48 66 49 67 console.log(`You selected ${framework}`); 50 68 ``` 69 + </WebContainer> 51 70 52 71 ### Autocomplete Selection 53 72 73 + <WebContainer name="autocomplete-selection"> 54 74 ```ts twoslash 55 75 import { autocomplete, isCancel } from '@clack/prompts'; 56 76 57 77 const selectedPackage = await autocomplete({ 58 78 message: 'Search for a package:', 59 79 options: [ 80 + { value: 'astro', label: 'Astro', hint: 'The web framework for content-based sites' }, 60 81 { value: 'react', label: 'React', hint: 'A JavaScript library for building user interfaces' }, 61 82 { value: 'vue', label: 'Vue', hint: 'The Progressive JavaScript Framework' }, 62 83 { value: 'svelte', label: 'Svelte', hint: 'Cybernetically enhanced web apps' }, 63 84 { value: 'angular', label: 'Angular', hint: 'Platform for building mobile & desktop web apps' }, 64 - { value: 'next', label: 'Next.js', hint: 'The React Framework for Production' }, 65 85 ], 66 86 placeholder: 'Type to search...', 67 87 maxItems: 5, ··· 74 94 75 95 console.log(`Selected package: ${selectedPackage}`); 76 96 ``` 97 + </WebContainer> 77 98 78 99 ### Multi-select with Groups 79 100 101 + <WebContainer name="multiselect-group"> 80 102 ```ts twoslash 81 103 import { groupMultiselect, isCancel } from '@clack/prompts'; 82 104 ··· 108 130 109 131 console.log('Selected tools:', tools); 110 132 ``` 133 + </WebContainer> 111 134 112 135 ### Confirmation Dialog 113 136 137 + <WebContainer name="confirmation-dialog"> 114 138 ```ts twoslash 115 139 import { confirm, isCancel } from '@clack/prompts'; 116 140 ··· 129 153 console.log('Operation cancelled'); 130 154 } 131 155 ``` 156 + </WebContainer> 132 157 133 158 ## Advanced Examples 134 159 ··· 931 956 } 932 957 ``` 933 958 934 - For more examples and best practices, check out our [GitHub repository](https://github.com/bombshell-dev/clack/tree/main/examples/basic). 959 + For more examples and best practices, check out our [GitHub repository](https://github.com/bombshell-dev/clack/tree/main/examples/basic).
+163 -142
src/styles/starlight.css
··· 1 - :root { 2 - --sl-font: system-ui, sans-serif; 3 - --sl-font-heading: 'polymath-display', sans-serif; 4 - 5 - --sl-color-text-heading: var(--heading-fill); 6 - --sl-text-h1: var(--sl-text-6xl); 7 - --sl-color-accent-hsl: 311 100% 50%; 8 - --sl-color-accent-low: hsl(311 100% 50% / 0.5); 9 - --sl-color-accent: hsl(311 100% 50% / 0.9); 10 - --sl-color-accent-high: hsl(311 100% 50% / 1); 1 + @layer starlight, user; 11 2 12 - --sl-color-bg: var(--container-fill); 13 - --sl-color-bg-nav: var(--container-fill); 14 - --sl-color-bg-sidebar: var(--container-fill); 15 - --sl-color-bg-inline-code: var(--surface-inner-fill); 16 - --sl-color-bg-accent: var(--sl-color-accent-high); 17 - --sl-color-hairline-light: var(--surface-border-fill); 18 - --sl-color-hairline: var(--surface-border-fill); 19 - --sl-color-hairline-shade: var(--surface-border-fill); 3 + @layer user.overrides { 4 + :root { 5 + --sl-font: system-ui, sans-serif; 6 + --sl-font-heading: 'polymath-display', sans-serif; 20 7 21 - --sl-icon-color: var(--heading-fill); 22 - } 8 + --sl-color-text-heading: var(--heading-fill); 9 + --sl-text-h1: var(--sl-text-6xl); 10 + --sl-color-accent-hsl: 311 100% 50%; 11 + --sl-color-accent-low: hsl(311 100% 50% / 0.5); 12 + --sl-color-accent: hsl(311 100% 50% / 0.9); 13 + --sl-color-accent-high: hsl(311 100% 50% / 1); 23 14 24 - :is(h1, h2, h3, h4, h5, h6) { 25 - font-feature-settings: 'ss03' on, 'ss12' on; 26 - } 15 + --sl-color-bg: var(--container-fill); 16 + --sl-color-bg-nav: var(--container-fill); 17 + --sl-color-bg-sidebar: var(--container-fill); 18 + --sl-color-bg-inline-code: var(--surface-inner-fill); 19 + --sl-color-bg-accent: var(--sl-color-accent-high); 20 + --sl-color-hairline-light: var(--surface-border-fill); 21 + --sl-color-hairline: var(--surface-border-fill); 22 + --sl-color-hairline-shade: var(--surface-border-fill); 27 23 28 - :is(h1, .starlight-sidebar-topics) { 29 - font-feature-settings: 'ss01' on, 'ss03' on, 'ss09' on, 'ss10' on, 'ss12' on; 30 - font-family: var(--sl-font-heading); 31 - } 32 - :is(.starlight-sidebar-topics li) { 33 - font: inherit; 34 - font-feature-settings: inherit; 35 - text-transform: lowercase; 36 - } 24 + --sl-icon-color: var(--heading-fill); 25 + } 37 26 38 - :is(h1) { 39 - --c-border: var(--heading-fill); 40 - --c-start: var(--heading-fill); 41 - --c-end: var(--heading-muted); 42 - --s-border: 1px; 43 - -webkit-text-stroke-color: var(--c-border); 44 - -webkit-text-stroke-width: var(--s-border); 45 - background-clip: text; 46 - -webkit-background-clip: text; 47 - -webkit-text-fill-color: transparent; 48 - background-image: linear-gradient(to right, var(--c-start), var(--c-end)); 49 - letter-spacing: 1px; 50 - } 27 + :is(h1, h2, h3, h4, h5, h6) { 28 + font-feature-settings: 'ss03' on, 'ss12' on; 29 + } 51 30 52 - :is(h2, h3, h4, h5, h6) { 53 - --c-start: var(--heading-fill); 54 - --c-end: var(--heading-muted); 55 - background-clip: text; 56 - -webkit-background-clip: text; 57 - -webkit-text-fill-color: transparent; 58 - background-image: linear-gradient(to right, var(--c-start), var(--c-end)); 59 - } 31 + :is(h1, .starlight-sidebar-topics) { 32 + font-feature-settings: 'ss01' on, 'ss03' on, 'ss09' on, 'ss10' on, 'ss12' on; 33 + font-family: var(--sl-font-heading); 34 + } 60 35 61 - /* Target specific elements */ 62 - .sl-nav-link, 63 - .site-title, 64 - .sl-doc-title, 65 - header h1 { 66 - font-family: var(--sl-font-heading); 67 - } 36 + :is(.starlight-sidebar-topics li) { 37 + font: inherit; 38 + font-feature-settings: inherit; 39 + text-transform: lowercase; 40 + } 68 41 69 - a.site-title { 70 - color: var(--heading-fill); 71 - } 42 + :is(h1) { 43 + --c-border: var(--heading-fill); 44 + --c-start: var(--heading-fill); 45 + --c-end: var(--heading-muted); 46 + --s-border: 1px; 47 + -webkit-text-stroke-color: var(--c-border); 48 + -webkit-text-stroke-width: var(--s-border); 49 + background-clip: text; 50 + -webkit-background-clip: text; 51 + -webkit-text-fill-color: transparent; 52 + background-image: linear-gradient(to right, var(--c-start), var(--c-end)); 53 + letter-spacing: 1px; 54 + } 72 55 73 - starlight-toc nav > ul { 74 - --c-start: var(--heading-muted); 75 - --c-end: var(--separator-fill); 76 - border-left: 1px solid transparent; 77 - position: relative; 78 - &::before { 79 - content: ''; 80 - position: absolute; 81 - top: 0; 82 - bottom: 0; 83 - left: 0; 84 - width: var(--border-050); 85 - background: linear-gradient(to bottom, var(--c-start), var(--c-end)); 56 + :is(h2, h3, h4, h5, h6) { 57 + --c-start: var(--heading-fill); 58 + --c-end: var(--heading-muted); 59 + background-clip: text; 60 + -webkit-background-clip: text; 61 + -webkit-text-fill-color: transparent; 62 + background-image: linear-gradient(to right, var(--c-start), var(--c-end)); 86 63 } 87 - a { 88 - border-left: 2px solid transparent; 89 - border-radius: 0; 64 + 65 + /* Target specific elements */ 66 + .sl-nav-link, 67 + .site-title, 68 + .sl-doc-title, 69 + header h1 { 70 + font-family: var(--sl-font-heading); 90 71 } 91 - [aria-current] { 92 - border-left-color: var(--sl-color-accent); 72 + 73 + a.site-title { 74 + color: var(--heading-fill); 93 75 } 94 - } 95 76 96 - p, 97 - li, 98 - td, 99 - blockquote { 100 - font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; 101 - } 77 + starlight-toc nav>ul { 78 + --c-start: var(--heading-muted); 79 + --c-end: var(--separator-fill); 80 + border-left: 1px solid transparent; 81 + position: relative; 102 82 103 - .cli-preview { 104 - padding: 1em; 105 - border: 1px solid #292929; 106 - border-radius: 0.5em; 107 - white-space: pre; 108 - line-height: 1.2em; 109 - font-family: monospace; 110 - background-color: #181818; 111 - color: var(--color-white-100); 112 - } 83 + &::before { 84 + content: ''; 85 + position: absolute; 86 + top: 0; 87 + bottom: 0; 88 + left: 0; 89 + width: var(--border-050); 90 + background: linear-gradient(to bottom, var(--c-start), var(--c-end)); 91 + } 113 92 114 - .sidebar-pane { 115 - a { 116 - border: 1px solid transparent; 117 - } 118 - [aria-current="page"] { 119 - --c-start: hsl(var(--sl-color-accent-hsl) / 5%); 120 - --c-end: hsl(var(--sl-color-accent-hsl) / 30%); 121 - color: var(--sl-color-accent); 122 - background: linear-gradient(to right, var(--c-start), var(--c-end)); 123 - border-color: var(--sl-color-accent); 93 + a { 94 + border-left: 2px solid transparent; 95 + border-radius: 0; 96 + } 124 97 125 - &:hover { 126 - --c-start: hsl(var(--sl-color-accent-hsl) / 100%); 127 - --c-end: hsl(var(--sl-color-accent-hsl) / 50%); 128 - border-color: var(--sl-color-accent); 129 - color: var(--color-gray-100); 130 - background: linear-gradient(to right, var(--c-start), var(--c-end)); 98 + [aria-current] { 99 + border-left-color: var(--sl-color-accent); 131 100 } 132 101 } 133 - } 134 102 135 - starlight-menu-button button, 136 - mobile-starlight-toc nav { 137 - top: unset; 138 - bottom: 0; 139 - } 103 + p, 104 + li, 105 + td, 106 + blockquote { 107 + font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; 108 + } 140 109 141 - /* Light theme specific overrides for better readability */ 142 - :root[data-theme=light] { 143 - --sl-color-text: var(--color-gray-100); 144 - --sl-color-text-muted: var(--color-gray-70); 145 - --sl-color-text-accent: var(--color-gray-90); 146 - 147 - .sl-nav-link, 148 - .site-title, 149 - .sl-doc-title { 110 + .sl-link-button.primary { 111 + background: linear-gradient(to right in oklab, var(--color-pink-90), var(--color-pink-100)); 150 112 color: var(--color-gray-100); 113 + border-color: var(--color-pink-100); 114 + border-radius: 0px; 151 115 } 152 - 153 - input::placeholder { 154 - color: var(--color-gray-50); 116 + 117 + .sl-link-button > svg { 118 + fill: var(--color-gray-100); 155 119 } 156 - 157 - --sl-icon-color: var(--color-gray-100); 158 - 159 - .sl-link-button { 160 - background-color: var(--color-gray-100); 120 + 121 + .cli-preview { 122 + padding: 1em; 123 + border: 1px solid #292929; 124 + border-radius: 0.5em; 125 + white-space: pre; 126 + line-height: 1.2em; 127 + font-family: monospace; 128 + background-color: #181818; 161 129 color: var(--color-white-100); 162 - border-color: var(--color-gray-100); 163 130 } 164 - .sl-link-button > svg { 165 - fill: var(--color-white-100); 131 + 132 + .sidebar-pane { 133 + a { 134 + border: 1px solid transparent; 135 + } 136 + 137 + [aria-current="page"] { 138 + --c-start: hsl(var(--sl-color-accent-hsl) / 5%); 139 + --c-end: hsl(var(--sl-color-accent-hsl) / 30%); 140 + color: var(--sl-color-accent); 141 + background: linear-gradient(to right, var(--c-start), var(--c-end)); 142 + border-color: var(--sl-color-accent); 143 + 144 + &:hover { 145 + --c-start: hsl(var(--sl-color-accent-hsl) / 100%); 146 + --c-end: hsl(var(--sl-color-accent-hsl) / 50%); 147 + border-color: var(--sl-color-accent); 148 + color: var(--color-gray-100); 149 + background: linear-gradient(to right, var(--c-start), var(--c-end)); 150 + } 151 + } 166 152 } 167 153 168 - .starlight-sidebar-topics-icon{ 169 - background-color: var(--color-gray-20); 154 + starlight-menu-button button, 155 + mobile-starlight-toc nav { 156 + top: unset; 157 + bottom: 0; 158 + } 159 + 160 + /* Light theme specific overrides for better readability */ 161 + :root[data-theme=light] { 162 + --sl-color-text: var(--color-gray-100); 163 + --sl-color-text-muted: var(--color-gray-70); 164 + --sl-color-text-accent: var(--color-gray-90); 165 + 166 + .sl-nav-link, 167 + .site-title, 168 + .sl-doc-title { 169 + color: var(--color-gray-100); 170 + } 171 + 172 + input::placeholder { 173 + color: var(--color-gray-50); 174 + } 175 + 176 + --sl-icon-color: var(--color-gray-100); 177 + 178 + .sl-link-button { 179 + background-color: var(--color-gray-100); 180 + color: var(--color-white-100); 181 + border-color: var(--color-gray-100); 182 + } 183 + 184 + .sl-link-button>svg { 185 + fill: var(--color-white-100); 186 + } 187 + 188 + .starlight-sidebar-topics-icon { 189 + background-color: var(--color-gray-20); 190 + } 170 191 } 171 192 }