π¨ docs: sci-fi design system β 5 themes (dark/light/dracula/rose-pine/warm), light-mode fix, code highlighting, animated landing with terminal + stat matrix + SVG viz
swadhinbiswas committed 2 months ago
227afa4
1 parent 0436ae9
commit 227afa4
5 files changed
+808 -341
Showing 5 changed files+808-341
βΌdocs_site/astro.config.mjs
+39-0
| @@ -10,6 +10,12 @@ export default defineConfig({ | ||
10 | 10 | integrations: [ |
11 | 11 | starlight({ |
12 | 12 | title: 'JustAPI', |
13 | + expressiveCode: { | |
14 | + themes: ['dracula', 'github-light'], | |
15 | + styleOverrides: { | |
16 | + borderRadius: '12px', | |
17 | + }, | |
18 | + }, | |
13 | 19 | description: 'Python web framework. Rust engine underneath.', |
14 | 20 | logo: { |
15 | 21 | src: './src/assets/logo.svg', |
| @@ -24,6 +30,39 @@ export default defineConfig({ | ||
24 | 30 | baseUrl: 'https://github.com/swadhinbiswas/JustAPI/edit/main/docs_site', |
25 | 31 | }, |
26 | 32 | head: [ |
33 | + { | |
34 | + tag: 'script', | |
35 | + attrs: { is: 'inline' }, | |
36 | + content: `// JustAPI theme switcher (dark/light/dracula/rose-pine/warm) | |
37 | +(function () { | |
38 | + var THEMES = ['dark', 'light', 'dracula', 'rose-pine', 'warm']; | |
39 | + function apply(t) { | |
40 | + document.documentElement.setAttribute('data-theme', t); | |
41 | + try { localStorage.setItem('ja-theme', t); } catch (e) {} | |
42 | + document.querySelectorAll('.ja-theme-btn').forEach(function (b) { | |
43 | + b.setAttribute('data-active', b.dataset.theme === t ? 'true' : 'false'); | |
44 | + }); | |
45 | + } | |
46 | + document.addEventListener('DOMContentLoaded', function () { | |
47 | + var saved = null; | |
48 | + try { saved = localStorage.getItem('ja-theme'); } catch (e) {} | |
49 | + if (saved && THEMES.indexOf(saved) !== -1) apply(saved); | |
50 | + var w = document.createElement('div'); | |
51 | + w.className = 'ja-theme-switcher'; | |
52 | + THEMES.forEach(function (t) { | |
53 | + var b = document.createElement('button'); | |
54 | + b.className = 'ja-theme-btn ja-theme-btn--' + (t === 'rose-pine' ? 'rosepine' : t); | |
55 | + b.dataset.theme = t; | |
56 | + b.title = t; | |
57 | + b.setAttribute('aria-label', 'Theme: ' + t); | |
58 | + b.addEventListener('click', function () { apply(t); }); | |
59 | + w.appendChild(b); | |
60 | + }); | |
61 | + document.body.appendChild(w); | |
62 | + apply(saved || (window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark')); | |
63 | + }); | |
64 | +})();`, | |
65 | + }, | |
27 | 66 | { tag: 'meta', attrs: { charset: 'utf-8' } }, |
28 | 67 | { tag: 'meta', attrs: { name: 'viewport', content: 'width=device-width, initial-scale=1' } }, |
29 | 68 | { tag: 'meta', attrs: { name: 'generator', content: 'Astro v7 + Starlight' } }, |
30 | 69 | |
βΌdocs_site/public/justapi-features.png
+0-0
Binary file not shown.
βΌdocs_site/public/justapi-pipeline.svg
+150-0
| @@ -0,0 +1,150 @@ | ||
1 | +<svg width="1200" height="420" viewBox="0 0 1200 420" fill="none" xmlns="http://www.w3.org/2000/svg"> | |
2 | + <defs> | |
3 | + <linearGradient id="bg2" x1="0" y1="0" x2="1200" y2="420" gradientUnits="userSpaceOnUse"> | |
4 | + <stop stop-color="#0b0e1a"/> | |
5 | + <stop offset="1" stop-color="#111631"/> | |
6 | + </linearGradient> | |
7 | + <linearGradient id="boxRust" x1="0" y1="0" x2="0" y2="1"> | |
8 | + <stop offset="0" stop-color="#e6522c"/> | |
9 | + <stop offset="1" stop-color="#b03a1c"/> | |
10 | + </linearGradient> | |
11 | + <linearGradient id="boxPy" x1="0" y1="0" x2="0" y2="1"> | |
12 | + <stop offset="0" stop-color="#22d3ee"/> | |
13 | + <stop offset="1" stop-color="#0e7490"/> | |
14 | + </linearGradient> | |
15 | + <filter id="glow2" x="-30%" y="-30%" width="160%" height="160%"> | |
16 | + <feDropShadow dx="0" dy="0" stdDeviation="6" flood-color="#22d3ee" flood-opacity="0.5"/> | |
17 | + </filter> | |
18 | + </defs> | |
19 | + | |
20 | + <rect width="1200" height="420" fill="url(#bg2)"/> | |
21 | + | |
22 | + <text x="600" y="40" text-anchor="middle" font-family="'Segoe UI', Arial, sans-serif" font-size="24" font-weight="700" fill="#e2e8f0" letter-spacing="1">One request, end to end</text> | |
23 | + | |
24 | + <!-- ============ ROW 1: Rust pipeline ============ --> | |
25 | + <g font-family="'Segoe UI', Arial, sans-serif"> | |
26 | + <!-- boxes: x positions --> | |
27 | + <!-- Kernel --> | |
28 | + <rect x="20" y="80" width="150" height="62" rx="10" fill="#1e293b" stroke="#334155"/> | |
29 | + <text x="95" y="105" text-anchor="middle" font-size="14" font-weight="700" fill="#22d3ee">Kernel</text> | |
30 | + <text x="95" y="125" text-anchor="middle" font-size="11" fill="#94a3b8">epoll / io_uring</text> | |
31 | + | |
32 | + <!-- tokio --> | |
33 | + <rect x="220" y="80" width="150" height="62" rx="10" fill="#1e293b" stroke="#334155"/> | |
34 | + <text x="295" y="105" text-anchor="middle" font-size="14" font-weight="700" fill="#22d3ee">tokio</text> | |
35 | + <text x="295" y="125" text-anchor="middle" font-size="11" fill="#94a3b8">async runtime</text> | |
36 | + | |
37 | + <!-- TLS --> | |
38 | + <rect x="420" y="80" width="150" height="62" rx="10" fill="#1e293b" stroke="#334155"/> | |
39 | + <text x="495" y="105" text-anchor="middle" font-size="14" font-weight="700" fill="#22d3ee">TLS</text> | |
40 | + <text x="495" y="125" text-anchor="middle" font-size="11" fill="#94a3b8">rustls</text> | |
41 | + | |
42 | + <!-- HTTP --> | |
43 | + <rect x="620" y="80" width="150" height="62" rx="10" fill="#1e293b" stroke="#334155"/> | |
44 | + <text x="695" y="105" text-anchor="middle" font-size="14" font-weight="700" fill="#22d3ee">HTTP/1.1 Β· 2</text> | |
45 | + <text x="695" y="125" text-anchor="middle" font-size="11" fill="#94a3b8">hyper</text> | |
46 | + | |
47 | + <!-- Router --> | |
48 | + <rect x="820" y="80" width="150" height="62" rx="10" fill="#1e293b" stroke="#334155"/> | |
49 | + <text x="895" y="105" text-anchor="middle" font-size="14" font-weight="700" fill="#22d3ee">Router</text> | |
50 | + <text x="895" y="125" text-anchor="middle" font-size="11" fill="#94a3b8">matchit Β· 51ns</text> | |
51 | + | |
52 | + <!-- Middleware --> | |
53 | + <rect x="1020" y="80" width="160" height="62" rx="10" fill="#1e293b" stroke="#334155"/> | |
54 | + <text x="1100" y="105" text-anchor="middle" font-size="14" font-weight="700" fill="#22d3ee">Middleware</text> | |
55 | + <text x="1100" y="125" text-anchor="middle" font-size="11" fill="#94a3b8">auth Β· CORS Β· rate-limit</text> | |
56 | + | |
57 | + <!-- arrows row 1 --> | |
58 | + <line x1="170" y1="111" x2="218" y2="111" stroke="#22d3ee" stroke-width="2" marker-end="url(#arr)"> | |
59 | + <animate attributeName="stroke-opacity" values="0.2;1;0.2" dur="2s" repeatCount="indefinite"/> | |
60 | + </line> | |
61 | + <line x1="370" y1="111" x2="418" y2="111" stroke="#22d3ee" stroke-width="2"> | |
62 | + <animate attributeName="stroke-opacity" values="0.2;1;0.2" dur="2.3s" repeatCount="indefinite"/> | |
63 | + </line> | |
64 | + <line x1="570" y1="111" x2="618" y2="111" stroke="#22d3ee" stroke-width="2"> | |
65 | + <animate attributeName="stroke-opacity" values="0.2;1;0.2" dur="2.6s" repeatCount="indefinite"/> | |
66 | + </line> | |
67 | + <line x1="770" y1="111" x2="818" y2="111" stroke="#22d3ee" stroke-width="2"> | |
68 | + <animate attributeName="stroke-opacity" values="0.2;1;0.2" dur="2.9s" repeatCount="indefinite"/> | |
69 | + </line> | |
70 | + <line x1="970" y1="111" x2="1018" y2="111" stroke="#22d3ee" stroke-width="2"> | |
71 | + <animate attributeName="stroke-opacity" values="0.2;1;0.2" dur="3.2s" repeatCount="indefinite"/> | |
72 | + </line> | |
73 | + <marker id="arr" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"> | |
74 | + <path d="M0 0 L10 5 L0 10 Z" fill="#22d3ee"/> | |
75 | + </marker> | |
76 | + | |
77 | + <!-- moving packet dots --> | |
78 | + <circle cx="170" cy="111" r="4" fill="#22d3ee" filter="url(#glow2)"> | |
79 | + <animateMotion dur="2s" repeatCount="indefinite" path="M170,111 L218,111"/> | |
80 | + </circle> | |
81 | + <circle cx="370" cy="111" r="4" fill="#22d3ee" filter="url(#glow2)"> | |
82 | + <animateMotion dur="2.3s" repeatCount="indefinite" path="M370,111 L418,111"/> | |
83 | + </circle> | |
84 | + <circle cx="570" cy="111" r="4" fill="#22d3ee" filter="url(#glow2)"> | |
85 | + <animateMotion dur="2.6s" repeatCount="indefinite" path="M570,111 L618,111"/> | |
86 | + </circle> | |
87 | + <circle cx="770" cy="111" r="4" fill="#22d3ee" filter="url(#glow2)"> | |
88 | + <animateMotion dur="2.9s" repeatCount="indefinite" path="M770,111 L818,111"/> | |
89 | + </circle> | |
90 | + <circle cx="970" cy="111" r="4" fill="#22d3ee" filter="url(#glow2)"> | |
91 | + <animateMotion dur="3.2s" repeatCount="indefinite" path="M970,111 L1018,111"/> | |
92 | + </circle> | |
93 | + </g> | |
94 | + | |
95 | + <!-- down arrow from middleware --> | |
96 | + <path d="M1100,142 L1100,170 L1000,170 L1000,195" stroke="#8b5cf6" stroke-width="2.5" fill="none" stroke-dasharray="6 5"> | |
97 | + <animate attributeName="stroke-dashoffset" values="0;-22" dur="1.5s" repeatCount="indefinite"/> | |
98 | + </path> | |
99 | + | |
100 | + <!-- ============ PYTHON BOUNDARY (glowing) ============ --> | |
101 | + <g font-family="'Segoe UI', Arial, sans-serif"> | |
102 | + <rect x="820" y="200" width="360" height="72" rx="12" fill="url(#boxPy)" filter="url(#glow2)"> | |
103 | + <animate attributeName="opacity" values="0.85;1;0.85" dur="3s" repeatCount="indefinite"/> | |
104 | + </rect> | |
105 | + <text x="1000" y="228" text-anchor="middle" font-size="16" font-weight="700" fill="#03141f">Python boundary</text> | |
106 | + <text x="1000" y="250" text-anchor="middle" font-size="12" fill="#0b3a4d">zero-copy PyO3 Β· GIL pool Β· free-threaded ready</text> | |
107 | + </g> | |
108 | + | |
109 | + <!-- down to handler --> | |
110 | + <path d="M1000,272 L1000,300 L700,300 L700,323" stroke="#8b5cf6" stroke-width="2.5" fill="none" stroke-dasharray="6 5"> | |
111 | + <animate attributeName="stroke-dashoffset" values="0;-22" dur="1.8s" repeatCount="indefinite"/> | |
112 | + </path> | |
113 | + | |
114 | + <!-- ============ YOUR CODE (highlighted) ============ --> | |
115 | + <g font-family="'Segoe UI', Arial, sans-serif"> | |
116 | + <rect x="560" y="325" width="280" height="72" rx="12" fill="#ffffff" fill-opacity="0.05" stroke="#f5a524" stroke-width="2"> | |
117 | + <animate attributeName="stroke-opacity" values="0.5;1;0.5" dur="2.2s" repeatCount="indefinite"/> | |
118 | + </rect> | |
119 | + <text x="700" y="350" text-anchor="middle" font-size="15" font-weight="700" fill="#f5a524">Your Python code</text> | |
120 | + <text x="700" y="372" text-anchor="middle" font-size="12" fill="#94a3b8">the only part that is yours</text> | |
121 | + </g> | |
122 | + | |
123 | + <!-- path from handler to serializer --> | |
124 | + <path d="M700,325 L700,300 L300,300 L300,272" stroke="#e6522c" stroke-width="2.5" fill="none" stroke-dasharray="6 5"> | |
125 | + <animate attributeName="stroke-dashoffset" values="0;-22" dur="1.6s" repeatCount="indefinite"/> | |
126 | + </path> | |
127 | + | |
128 | + <!-- ============ ROW 2: response path ============ --> | |
129 | + <g font-family="'Segoe UI', Arial, sans-serif"> | |
130 | + <rect x="20" y="200" width="240" height="62" rx="10" fill="#1e293b" stroke="#334155"/> | |
131 | + <text x="140" y="225" text-anchor="middle" font-size="14" font-weight="700" fill="#e6522c">Serializer</text> | |
132 | + <text x="140" y="245" text-anchor="middle" font-size="11" fill="#94a3b8">serde_json Β· simd-json</text> | |
133 | + | |
134 | + <rect x="20" y="325" width="240" height="62" rx="10" fill="#1e293b" stroke="#334155"/> | |
135 | + <text x="140" y="350" text-anchor="middle" font-size="14" font-weight="700" fill="#22d3ee">Response write</text> | |
136 | + <text x="140" y="370" text-anchor="middle" font-size="11" fill="#94a3b8">back to the socket</text> | |
137 | + | |
138 | + <line x1="140" y1="262" x2="140" y2="323" stroke="#e6522c" stroke-width="2" stroke-dasharray="6 5"> | |
139 | + <animate attributeName="stroke-dashoffset" values="0;-22" dur="1.4s" repeatCount="indefinite"/> | |
140 | + </line> | |
141 | + </g> | |
142 | + | |
143 | + <!-- side legend --> | |
144 | + <g font-family="'Segoe UI', Arial, sans-serif" font-size="12"> | |
145 | + <rect x="420" y="225" width="16" height="16" rx="4" fill="#22d3ee"/> | |
146 | + <text x="445" y="238" fill="#94a3b8">Rust (everything except your code)</text> | |
147 | + <rect x="420" y="252" width="16" height="16" rx="4" fill="#f5a524"/> | |
148 | + <text x="445" y="265" fill="#94a3b8">Python (just your logic)</text> | |
149 | + </g> | |
150 | +</svg> | |
0 | 151 | |
βΌdocs_site/src/content/docs/index.mdx
+121-94
| @@ -1,9 +1,9 @@ | ||
1 | 1 | --- |
2 | 2 | title: JustAPI |
3 | -description: Python web framework powered by Rust. Handles 700k+ requests per second. FastAPI alternative with 20Γ performance. | |
3 | +description: Python web framework powered by Rust. 766k req/s. Built to handle real traffic. | |
4 | 4 | template: splash |
5 | 5 | hero: |
6 | - tagline: Python web framework powered by Rust. Built to handle real traffic. | |
6 | + tagline: Python web framework. Rust engine underneath. | |
7 | 7 | image: |
8 | 8 | file: ../../assets/logo.svg |
9 | 9 | actions: |
| @@ -11,101 +11,128 @@ hero: | ||
11 | 11 | link: /getting-started/overview/ |
12 | 12 | icon: right-arrow |
13 | 13 | variant: primary |
14 | - - text: View on GitHub | |
14 | + - text: GitHub | |
15 | 15 | link: https://github.com/swadhinbiswas/JustAPI |
16 | 16 | icon: external |
17 | 17 | variant: minimal |
18 | 18 | --- |
19 | 19 | |
20 | -import { Card, CardGrid } from '@astrojs/starlight/components'; | |
21 | - | |
22 | -## Performance | |
23 | - | |
24 | -| Framework | Hello-world req/s | JSON echo | p99 latency | RSS | | |
25 | -|---|--:|--:|--:|--:| | |
26 | -| **JustAPI (native fast path)** | **766,000** | **782,000** | **0.48 ms** | **12 MB** | | |
27 | -| Granian (ASGI) | 314,000 | 145,000 | 0.74 ms | 37 MB | | |
28 | -| Robyn | 39,000 | β | β | β | | |
29 | -| FastAPI + Uvicorn | 36,000 | 33,000 | 24.63 ms | 29 MB | | |
30 | - | |
31 | -All measurements on the same hardware, 100 concurrent connections, 30s duration. Full methodology in [BENCHMARKS.md](https://github.com/swadhinbiswas/JustAPI/blob/main/BENCHMARKS.md). | |
32 | - | |
33 | -## What It Does | |
34 | - | |
35 | -JustAPI runs your Python application logic on top of a Rust networking stack. Routing, TLS, serialization, compression β all handled in Rust before your code even runs. | |
36 | - | |
37 | -```python title="main.py" | |
38 | -from justapi import JustAPIApp | |
39 | - | |
40 | -app = JustAPIApp() | |
41 | - | |
42 | -@app.get("/") | |
43 | -def hello(): | |
44 | - return {"message": "Hello from JustAPI!"} | |
45 | -``` | |
46 | - | |
47 | -```bash title="Terminal" | |
48 | -$ pip install justapi | |
49 | -$ python main.py | |
50 | - | |
51 | -INFO: JustAPI running on http://127.0.0.1:8000 | |
52 | -``` | |
53 | - | |
54 | -Open `/docs` for Swagger UI, `/redoc` for ReDoc, and the full OpenAPI schema β generated automatically from your code. | |
55 | - | |
56 | -## Features | |
57 | - | |
58 | -<CardGrid> | |
59 | - <Card title="FastAPI-Compatible" icon="star"> | |
60 | - Same decorators, Pydantic models, dependency injection. Switch with one import. | |
61 | - </Card> | |
62 | - <Card title="Rust Under the Hood" icon="rocket"> | |
63 | - Networking, TLS, routing, serialization run in Rust. Python does the rest. | |
64 | - </Card> | |
65 | - <Card title="Zero GIL" icon="shield"> | |
66 | - GIL released during I/O. Works with free-threaded Python 3.13t/3.14t. | |
67 | - </Card> | |
68 | - <Card title="Production-Ready" icon="lock"> | |
69 | - JWT auth, rate limiting, circuit breakers, OpenTelemetry, health checks. | |
70 | - </Card> | |
71 | - <Card title="Multi-Protocol" icon="globe"> | |
72 | - REST, GraphQL, gRPC, JSON-RPC 2.0, WebSocket, SSE β all in one app. | |
73 | - </Card> | |
74 | - <Card title="Native Async DB" icon="rocket"> | |
75 | - `await app.db.query_async()` β SQL runs on Rust's tokio runtime, GIL released. 53Γ faster on slow queries. | |
76 | - </Card> | |
77 | - <Card title="Rust-Native SSE" icon="activity"> | |
20 | +<div class="ja-hero"> | |
21 | + <img src="/og-image.png" alt="JustAPI β Python writes the logic. Rust does everything else." width="100%" /> | |
22 | +</div> | |
23 | + | |
24 | +## Performance Matrix | |
25 | + | |
26 | +<div class="benchmark-card"> | |
27 | + <div class="ja-stats"> | |
28 | + <div class="ja-stat"> | |
29 | + <div class="ja-stat-value">766k</div> | |
30 | + <div class="ja-stat-label">req/s hello-world</div> | |
31 | + </div> | |
32 | + <div class="ja-stat"> | |
33 | + <div class="ja-stat-value">782k</div> | |
34 | + <div class="ja-stat-label">req/s JSON echo</div> | |
35 | + </div> | |
36 | + <div class="ja-stat"> | |
37 | + <div class="ja-stat-value">2.4x</div> | |
38 | + <div class="ja-stat-label">vs Granian</div> | |
39 | + </div> | |
40 | + <div class="ja-stat"> | |
41 | + <div class="ja-stat-value">53x</div> | |
42 | + <div class="ja-stat-label">native async DB</div> | |
43 | + </div> | |
44 | + <div class="ja-stat"> | |
45 | + <div class="ja-stat-value">12MB</div> | |
46 | + <div class="ja-stat-label">resident memory</div> | |
47 | + </div> | |
48 | + <div class="ja-stat"> | |
49 | + <div class="ja-stat-value">12.4x</div> | |
50 | + <div class="ja-stat-label">free-threaded CPU</div> | |
51 | + </div> | |
52 | + </div> | |
53 | +</div> | |
54 | + | |
55 | +## Runtime | |
56 | + | |
57 | +<div class="ja-terminal"> | |
58 | + <div class="ja-terminal-bar"> | |
59 | + <span class="ja-terminal-dot"></span> | |
60 | + <span class="ja-terminal-dot"></span> | |
61 | + <span class="ja-terminal-dot"></span> | |
62 | + </div> | |
63 | + <div class="ja-terminal-body"> | |
64 | + <span class="cm">$</span> pip install justapi<br/> | |
65 | + <span class="ok">β installed 2.0.9 (8 platforms)</span><br/> | |
66 | + <span class="cm">$</span> cat main.py<br/> | |
67 | + <span class="cm">from</span> justapi <span class="cm">import</span> JustAPIApp<br/> | |
68 | + <br/> | |
69 | + app = JustAPIApp()<br/> | |
70 | + <br/> | |
71 | + <span class="cm">@app.get</span>(<span class="ok">"/"</span>)<br/> | |
72 | + <span class="cm">def</span> hello():<br/> | |
73 | + <span class="cm">return</span> {<span class="ok">"Hello"</span>: <span class="ok">"World"</span>}<br/> | |
74 | + <br/> | |
75 | + app.run()<br/> | |
76 | + <span class="ok">β server started on http://127.0.0.1:8000</span> | |
77 | + </div> | |
78 | +</div> | |
79 | + | |
80 | +## Feature Graph | |
81 | + | |
82 | +<div class="ja-graph"> | |
83 | + <img src="/justapi-features.png" alt="JustAPI feature matrix" width="100%" /> | |
84 | +</div> | |
85 | + | |
86 | +## Pipeline | |
87 | + | |
88 | +<div class="ja-graph"> | |
89 | + <img src="/justapi-pipeline.svg" alt="Request pipeline β Rust end to end" width="100%" /> | |
90 | +</div> | |
91 | + | |
92 | +## Why JustAPI | |
93 | + | |
94 | +<div class="feature-grid"> | |
95 | + <div class="feature-card"> | |
96 | + <h3>Rust Core</h3> | |
97 | + Networking, TLS, routing, serialization β all in Rust before your code runs. | |
98 | + </div> | |
99 | + <div class="feature-card"> | |
100 | + <h3>Native Async DB</h3> | |
101 | + `await app.db.query_async()` β SQL on Rust's tokio runtime, loop never blocked. | |
102 | + </div> | |
103 | + <div class="feature-card"> | |
104 | + <h3>Rust-Native SSE</h3> | |
78 | 105 | `app.sse_native()` streams events with zero Python per event. |
79 | - </Card> | |
80 | - <Card title="Free-Threathed 3.14t" icon="shield"> | |
81 | - CPU-bound Python scales with cores β 12.4Γ measured. | |
82 | - </Card> | |
83 | - <Card title="Auto Docs" icon="document"> | |
84 | - Swagger UI, ReDoc, Scalar. Generated from your code. | |
85 | - </Card> | |
86 | -</CardGrid> | |
87 | - | |
88 | -## Install | |
89 | - | |
90 | -```bash title="Terminal" | |
91 | -$ pip install justapi | |
92 | -$ uv pip install justapi | |
93 | -$ uvx justapi create my_app | |
94 | -``` | |
95 | - | |
96 | -## Learn | |
97 | - | |
98 | -<CardGrid> | |
99 | - <Card title="Tutorial" icon="pencil"> | |
100 | - [36-step learning path β](/tutorials/python-types/) | |
101 | - </Card> | |
102 | - <Card title="Advanced" icon="rocket"> | |
103 | - [Rust internals, zero-GIL, native fast path β](/advanced/zero-gil-architecture/) | |
104 | - </Card> | |
105 | - <Card title="API Reference" icon="document"> | |
106 | - [Every class and function β](/api-reference/) | |
107 | - </Card> | |
108 | - <Card title="Deployment" icon="setting"> | |
109 | - [Docker, Kubernetes, cloud providers β](/deployment/docker/) | |
110 | - </Card> | |
111 | -</CardGrid> | |
106 | + </div> | |
107 | + <div class="feature-card"> | |
108 | + <h3>Multi-Worker</h3> | |
109 | + `justapi serve --workers N --scale` β 1.88x throughput at 4 workers. | |
110 | + </div> | |
111 | + <div class="feature-card"> | |
112 | + <h3>Free-Threaded</h3> | |
113 | + CPython 3.14t wheels β CPU-bound Python scales with cores. | |
114 | + </div> | |
115 | + <div class="feature-card"> | |
116 | + <h3>Type-Checked</h3> | |
117 | + `py.typed` + complete stubs β mypy-clean on user code. | |
118 | + </div> | |
119 | +</div> | |
120 | + | |
121 | +## Framework vs FastAPI | |
122 | + | |
123 | +| | FastAPI | JustAPI | | |
124 | +|---|---|---| | |
125 | +| **Server** | uvicorn (Python) | Rust (tokio + hyper) | | |
126 | +| **Hello-world** | ~36k req/s | **~766k req/s** | | |
127 | +| **JSON echo** | ~33k req/s | **~782k req/s** | | |
128 | +| **Native DB SELECT** | Python driver | **181k req/s (Γ125)** | | |
129 | +| **Memory** | 29 MB | **12 MB** | | |
130 | +| **OpenAPI docs** | /docs | /docs | | |
131 | + | |
132 | +<div class="ja-cta"> | |
133 | + <div class="ja-cta-title">Ready to build?</div> | |
134 | + <p style={{ color: "var(--ja-muted)" }}>One import. Rust underneath. Zero config.</p> | |
135 | + <p> | |
136 | + <a href="/getting-started/overview/" class="ja-cta-btn" style={{ display: "inline-block", padding: "0.75rem 1.5rem", border: "1px solid var(--ja-accent)", borderRadius: "9999px", color: "var(--ja-accent)", fontWeight: 700 }}>Get Started β</a> | |
137 | + </p> | |
138 | +</div> | |
112 | 139 | |
βΌdocs_site/src/styles/custom.css
+498-247
| @@ -1,69 +1,189 @@ | ||
1 | 1 | /* βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
2 | - JustAPI Documentation β Design System | |
3 | - Premium, FastAPI-level documentation styling | |
2 | + JustAPI Documentation β Sci-Fi Design System | |
3 | + Themes: Sci-Fi Dark (default) Β· Light Β· Dracula Β· RosΓ© Pine Β· Warm | |
4 | + Palette: green #00ff9d Β· black #05080f Β· white #e6f1ff Β· red #ff2e4d | |
4 | 5 | βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */ |
5 | 6 | |
6 | -/* βββ Custom Properties βββ */ | |
7 | -:root { | |
8 | - /* Accent palette */ | |
9 | - --sl-color-accent-low: #2a1147; | |
10 | - --sl-color-accent: #8b5cf6; | |
11 | - --sl-color-accent-high: #c4b5fd; | |
12 | - | |
13 | - /* Neutrals */ | |
14 | - --sl-color-white: #ffffff; | |
15 | - --sl-color-gray-1: #f8fafc; | |
16 | - --sl-color-gray-2: #f1f5f9; | |
17 | - --sl-color-gray-3: #94a3b8; | |
18 | - --sl-color-gray-4: #64748b; | |
19 | - --sl-color-gray-5: #334155; | |
20 | - --sl-color-gray-6: #1e293b; | |
21 | - --sl-color-black: #020617; | |
22 | - | |
23 | - /* Brand */ | |
24 | - --sl-color-orange: #e6522c; | |
25 | - --sl-color-blue: #3b82f6; | |
26 | - --sl-color-green: #10b981; | |
27 | - --sl-color-red: #ef4444; | |
28 | - --sl-color-yellow: #f59e0b; | |
29 | - | |
30 | - /* Typography */ | |
31 | - --sl-font: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; | |
32 | - --sl-font-mono: 'JetBrains Mono', 'Fira Code', 'Cascadia Code', 'SF Mono', monospace; | |
33 | - | |
34 | - /* Spacing */ | |
35 | - --sl-content-width: 72rem; | |
36 | - --sl-sidebar-width: 18rem; | |
37 | - --sl-toc-width: 14rem; | |
38 | -} | |
39 | - | |
40 | -/* βββ Dark Mode βββ */ | |
7 | +/* βββ Base palette (Sci-Fi Dark) βββ */ | |
8 | +:root, | |
41 | 9 | [data-theme='dark'] { |
42 | - --sl-color-bg: #020617; | |
43 | - --sl-color-bg-nav: #0f172a; | |
44 | - --sl-color-bg-sidebar: #0f172a; | |
45 | - --sl-color-bg-hover: #1e293b; | |
46 | - --sl-color-text: #e2e8f0; | |
47 | - --sl-color-text-muted: #94a3b8; | |
48 | - --sl-color-border: #1e293b; | |
49 | -} | |
50 | - | |
51 | -/* βββ Light Mode βββ */ | |
10 | + --ja-bg: #05080f; | |
11 | + --ja-bg-2: #0a0f1c; | |
12 | + --ja-bg-3: #101830; | |
13 | + --ja-panel: rgba(10, 15, 28, 0.8); | |
14 | + --ja-border: #1a2440; | |
15 | + --ja-green: #00ff9d; | |
16 | + --ja-green-dim: #00c97d; | |
17 | + --ja-red: #ff2e4d; | |
18 | + --ja-white: #e6f1ff; | |
19 | + --ja-muted: #7d8db1; | |
20 | + --ja-accent: #00ff9d; | |
21 | + --ja-accent-2: #00d4ff; | |
22 | + | |
23 | + --sl-color-accent-low: #003d26; | |
24 | + --sl-color-accent: #00ff9d; | |
25 | + --sl-color-accent-high: #66ffc4; | |
26 | + --sl-color-white: #e6f1ff; | |
27 | + --sl-color-gray-1: #e6f1ff; | |
28 | + --sl-color-gray-2: #b8c6e0; | |
29 | + --sl-color-gray-3: #7d8db1; | |
30 | + --sl-color-gray-4: #55648c; | |
31 | + --sl-color-gray-5: #2a3550; | |
32 | + --sl-color-gray-6: #141c30; | |
33 | + --sl-color-black: #05080f; | |
34 | + --sl-color-bg: #05080f; | |
35 | + --sl-color-bg-nav: #070b15; | |
36 | + --sl-color-bg-sidebar: #070b15; | |
37 | + --sl-color-bg-hover: #0d1526; | |
38 | + --sl-color-text: #e6f1ff; | |
39 | + --sl-color-text-muted: #7d8db1; | |
40 | + --sl-color-border: #1a2440; | |
41 | +} | |
42 | + | |
43 | +/* βββ Light theme βββ */ | |
52 | 44 | [data-theme='light'] { |
53 | - --sl-color-bg: #ffffff; | |
54 | - --sl-color-bg-nav: #f8fafc; | |
55 | - --sl-color-bg-sidebar: #f8fafc; | |
56 | - --sl-color-bg-hover: #f1f5f9; | |
57 | - --sl-color-text: #1e293b; | |
58 | - --sl-color-text-muted: #64748b; | |
59 | - --sl-color-border: #e2e8f0; | |
60 | -} | |
61 | - | |
62 | -/* βββ Global Typography βββ */ | |
45 | + --ja-bg: #f4f7fb; | |
46 | + --ja-bg-2: #ffffff; | |
47 | + --ja-bg-3: #eef2f8; | |
48 | + --ja-panel: rgba(255, 255, 255, 0.92); | |
49 | + --ja-border: #d5dde8; | |
50 | + --ja-green: #00925c; | |
51 | + --ja-green-dim: #007a4c; | |
52 | + --ja-red: #d91f3c; | |
53 | + --ja-white: #0d1526; | |
54 | + --ja-muted: #5a6a8c; | |
55 | + --ja-accent: #00925c; | |
56 | + --ja-accent-2: #0077a8; | |
57 | + | |
58 | + --sl-color-accent-low: #e0f7ee; | |
59 | + --sl-color-accent: #00925c; | |
60 | + --sl-color-accent-high: #00593a; | |
61 | + --sl-color-white: #0d1526; | |
62 | + --sl-color-gray-1: #0d1526; | |
63 | + --sl-color-gray-2: #2a3550; | |
64 | + --sl-color-gray-3: #5a6a8c; | |
65 | + --sl-color-gray-4: #7d8db1; | |
66 | + --sl-color-gray-5: #d5dde8; | |
67 | + --sl-color-gray-6: #eef2f8; | |
68 | + --sl-color-black: #ffffff; | |
69 | + --sl-color-bg: #f4f7fb; | |
70 | + --sl-color-bg-nav: #ffffff; | |
71 | + --sl-color-bg-sidebar: #ffffff; | |
72 | + --sl-color-bg-hover: #eef2f8; | |
73 | + --sl-color-text: #0d1526; | |
74 | + --sl-color-text-muted: #5a6a8c; | |
75 | + --sl-color-border: #d5dde8; | |
76 | +} | |
77 | + | |
78 | +/* βββ Dracula theme βββ */ | |
79 | +[data-theme='dracula'] { | |
80 | + --ja-bg: #282a36; | |
81 | + --ja-bg-2: #21222c; | |
82 | + --ja-bg-3: #343746; | |
83 | + --ja-panel: rgba(33, 34, 44, 0.9); | |
84 | + --ja-border: #44475a; | |
85 | + --ja-green: #50fa7b; | |
86 | + --ja-green-dim: #3dd962; | |
87 | + --ja-red: #ff5555; | |
88 | + --ja-white: #f8f8f2; | |
89 | + --ja-muted: #6272a4; | |
90 | + --ja-accent: #bd93f9; | |
91 | + --ja-accent-2: #8be9fd; | |
92 | + | |
93 | + --sl-color-accent-low: #3d3552; | |
94 | + --sl-color-accent: #bd93f9; | |
95 | + --sl-color-accent-high: #e3d5ff; | |
96 | + --sl-color-white: #f8f8f2; | |
97 | + --sl-color-gray-1: #f8f8f2; | |
98 | + --sl-color-gray-2: #bfc2d8; | |
99 | + --sl-color-gray-3: #6272a4; | |
100 | + --sl-color-gray-4: #4d5a80; | |
101 | + --sl-color-gray-5: #343746; | |
102 | + --sl-color-gray-6: #21222c; | |
103 | + --sl-color-black: #282a36; | |
104 | + --sl-color-bg: #282a36; | |
105 | + --sl-color-bg-nav: #21222c; | |
106 | + --sl-color-bg-sidebar: #21222c; | |
107 | + --sl-color-bg-hover: #343746; | |
108 | + --sl-color-text: #f8f8f2; | |
109 | + --sl-color-text-muted: #6272a4; | |
110 | + --sl-color-border: #44475a; | |
111 | +} | |
112 | + | |
113 | +/* βββ RosΓ© Pine theme βββ */ | |
114 | +[data-theme='rose-pine'] { | |
115 | + --ja-bg: #191724; | |
116 | + --ja-bg-2: #1f1d2e; | |
117 | + --ja-bg-3: #26233a; | |
118 | + --ja-panel: rgba(31, 29, 46, 0.9); | |
119 | + --ja-border: #403d52; | |
120 | + --ja-green: #9ccfd8; | |
121 | + --ja-green-dim: #7fb4bd; | |
122 | + --ja-red: #eb6f92; | |
123 | + --ja-white: #e0def4; | |
124 | + --ja-muted: #908caa; | |
125 | + --ja-accent: #c4a7e7; | |
126 | + --ja-accent-2: #ebbcba; | |
127 | + | |
128 | + --sl-color-accent-low: #3d3552; | |
129 | + --sl-color-accent: #c4a7e7; | |
130 | + --sl-color-accent-high: #e0def4; | |
131 | + --sl-color-white: #e0def4; | |
132 | + --sl-color-gray-1: #e0def4; | |
133 | + --sl-color-gray-2: #b5b1d6; | |
134 | + --sl-color-gray-3: #908caa; | |
135 | + --sl-color-gray-4: #6e6a86; | |
136 | + --sl-color-gray-5: #403d52; | |
137 | + --sl-color-gray-6: #26233a; | |
138 | + --sl-color-black: #191724; | |
139 | + --sl-color-bg: #191724; | |
140 | + --sl-color-bg-nav: #1f1d2e; | |
141 | + --sl-color-bg-sidebar: #1f1d2e; | |
142 | + --sl-color-bg-hover: #26233a; | |
143 | + --sl-color-text: #e0def4; | |
144 | + --sl-color-text-muted: #908caa; | |
145 | + --sl-color-border: #403d52; | |
146 | +} | |
147 | + | |
148 | +/* βββ Warm theme βββ */ | |
149 | +[data-theme='warm'] { | |
150 | + --ja-bg: #1c1412; | |
151 | + --ja-bg-2: #241a17; | |
152 | + --ja-bg-3: #33241e; | |
153 | + --ja-panel: rgba(36, 26, 23, 0.9); | |
154 | + --ja-border: #4a362e; | |
155 | + --ja-green: #ffb454; | |
156 | + --ja-green-dim: #e89b3a; | |
157 | + --ja-red: #ff6b6b; | |
158 | + --ja-white: #f8ead9; | |
159 | + --ja-muted: #a88b7a; | |
160 | + --ja-accent: #ff9d5c; | |
161 | + --ja-accent-2: #ffd166; | |
162 | + | |
163 | + --sl-color-accent-low: #4a2c1a; | |
164 | + --sl-color-accent: #ff9d5c; | |
165 | + --sl-color-accent-high: #ffd0a8; | |
166 | + --sl-color-white: #f8ead9; | |
167 | + --sl-color-gray-1: #f8ead9; | |
168 | + --sl-color-gray-2: #d4bca8; | |
169 | + --sl-color-gray-3: #a88b7a; | |
170 | + --sl-color-gray-4: #7d6353; | |
171 | + --sl-color-gray-5: #4a362e; | |
172 | + --sl-color-gray-6: #33241e; | |
173 | + --sl-color-black: #1c1412; | |
174 | + --sl-color-bg: #1c1412; | |
175 | + --sl-color-bg-nav: #241a17; | |
176 | + --sl-color-bg-sidebar: #241a17; | |
177 | + --sl-color-bg-hover: #33241e; | |
178 | + --sl-color-text: #f8ead9; | |
179 | + --sl-color-text-muted: #a88b7a; | |
180 | + --sl-color-border: #4a362e; | |
181 | +} | |
182 | + | |
183 | +/* βββ Typography βββ */ | |
63 | 184 | html { |
64 | - font-family: var(--sl-font); | |
185 | + font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; | |
65 | 186 | -webkit-font-smoothing: antialiased; |
66 | - -moz-osx-font-smoothing: grayscale; | |
67 | 187 | text-rendering: optimizeLegibility; |
68 | 188 | scroll-behavior: smooth; |
69 | 189 | } |
| @@ -73,23 +193,31 @@ body { | ||
73 | 193 | letter-spacing: -0.011em; |
74 | 194 | } |
75 | 195 | |
76 | -/* βββ Headings βββ */ | |
77 | 196 | .content h1 { |
78 | 197 | font-size: 2.25rem; |
79 | 198 | font-weight: 800; |
80 | 199 | letter-spacing: -0.025em; |
81 | 200 | line-height: 1.2; |
82 | 201 | margin-bottom: 1rem; |
202 | + background: linear-gradient(90deg, var(--ja-white), var(--ja-accent)); | |
203 | + -webkit-background-clip: text; | |
204 | + -webkit-text-fill-color: transparent; | |
205 | + background-clip: text; | |
206 | +} | |
207 | + | |
208 | +[data-theme='light'] .content h1 { | |
209 | + background: linear-gradient(90deg, var(--ja-white), var(--ja-green)); | |
210 | + -webkit-background-clip: text; | |
211 | + background-clip: text; | |
83 | 212 | } |
84 | 213 | |
85 | 214 | .content h2 { |
86 | 215 | font-size: 1.5rem; |
87 | 216 | font-weight: 700; |
88 | - letter-spacing: -0.02em; | |
89 | 217 | margin-top: 3rem; |
90 | 218 | margin-bottom: 1rem; |
91 | 219 | padding-bottom: 0.75rem; |
92 | - border-bottom: 1px solid var(--sl-color-border, #e2e8f0); | |
220 | + border-bottom: 1px solid var(--sl-color-border); | |
93 | 221 | } |
94 | 222 | |
95 | 223 | .content h3 { |
| @@ -97,65 +225,68 @@ body { | ||
97 | 225 | font-weight: 600; |
98 | 226 | margin-top: 2rem; |
99 | 227 | margin-bottom: 0.75rem; |
100 | - letter-spacing: -0.01em; | |
101 | -} | |
102 | - | |
103 | -.content h4 { | |
104 | - font-size: 1rem; | |
105 | - font-weight: 600; | |
106 | - margin-top: 1.5rem; | |
107 | 228 | } |
108 | 229 | |
109 | 230 | /* βββ Links βββ */ |
110 | 231 | .content a { |
111 | - color: var(--sl-color-accent); | |
232 | + color: var(--ja-accent); | |
112 | 233 | text-decoration: none; |
113 | - transition: color 0.15s ease; | |
234 | + transition: color 0.15s ease, text-shadow 0.15s ease; | |
114 | 235 | } |
115 | 236 | |
116 | 237 | .content a:hover { |
117 | - color: var(--sl-color-accent-high); | |
238 | + color: var(--ja-accent-high); | |
118 | 239 | text-decoration: underline; |
119 | 240 | text-decoration-thickness: 2px; |
120 | 241 | text-underline-offset: 3px; |
242 | + text-shadow: 0 0 12px rgba(0, 255, 157, 0.4); | |
121 | 243 | } |
122 | 244 | |
123 | 245 | /* βββ Code Blocks βββ */ |
124 | 246 | code { |
125 | - font-family: var(--sl-font-mono); | |
247 | + font-family: 'JetBrains Mono', 'Fira Code', monospace; | |
126 | 248 | font-size: 0.85em; |
127 | 249 | border-radius: 6px; |
128 | 250 | padding: 0.15em 0.4em; |
129 | - background: var(--sl-color-gray-2, #f1f5f9); | |
251 | + background: var(--sl-color-gray-5); | |
252 | + color: var(--ja-accent); | |
130 | 253 | } |
131 | 254 | |
132 | -[data-theme='dark'] code { | |
133 | - background: rgba(139, 92, 246, 0.15); | |
134 | - color: #c4b5fd; | |
255 | +[data-theme='light'] code { | |
256 | + background: #e8f5ee; | |
257 | + color: #00925c; | |
135 | 258 | } |
136 | 259 | |
137 | 260 | pre { |
138 | - font-family: var(--sl-font-mono); | |
261 | + font-family: 'JetBrains Mono', 'Fira Code', monospace; | |
139 | 262 | font-size: 0.875rem; |
140 | 263 | line-height: 1.6; |
141 | 264 | border-radius: 12px; |
142 | - border: 1px solid var(--sl-color-border, #e2e8f0); | |
265 | + border: 1px solid var(--ja-border); | |
143 | 266 | padding: 1.25rem; |
144 | 267 | overflow-x: auto; |
145 | - background: #0f172a !important; | |
268 | + background: var(--ja-bg-2); | |
269 | + box-shadow: inset 0 0 30px rgba(0, 255, 157, 0.03); | |
146 | 270 | } |
147 | 271 | |
148 | -[data-theme='dark'] pre { | |
149 | - background: #0b0f19 !important; | |
150 | - border-color: #1e293b; | |
272 | +[data-theme='light'] pre { | |
273 | + background: #0d1526; | |
274 | + color: #e6f1ff; | |
151 | 275 | } |
152 | 276 | |
277 | +[data-theme='dracula'] pre { background: #21222c; } | |
278 | +[data-theme='rose-pine'] pre { background: #1f1d2e; } | |
279 | +[data-theme='warm'] pre { background: #241a17; } | |
280 | + | |
153 | 281 | pre code { |
154 | 282 | background: none; |
155 | 283 | padding: 0; |
156 | 284 | border-radius: 0; |
285 | + color: inherit; | |
157 | 286 | } |
158 | 287 | |
288 | +[data-theme='light'] pre code { color: #e6f1ff; } | |
289 | + | |
159 | 290 | /* βββ Tables βββ */ |
160 | 291 | .content table { |
161 | 292 | width: 100%; |
| @@ -164,43 +295,36 @@ pre code { | ||
164 | 295 | border-spacing: 0; |
165 | 296 | border-radius: 12px; |
166 | 297 | overflow: hidden; |
167 | - border: 1px solid var(--sl-color-border, #e2e8f0); | |
298 | + border: 1px solid var(--ja-border); | |
168 | 299 | margin: 1.5rem 0; |
169 | 300 | } |
170 | 301 | |
171 | 302 | .content thead { |
172 | - background: var(--sl-color-gray-6, #1e293b); | |
173 | -} | |
174 | - | |
175 | -[data-theme='dark'] .content thead { | |
176 | - background: #1e293b; | |
177 | -} | |
178 | - | |
179 | -[data-theme='light'] .content thead { | |
180 | - background: #f1f5f9; | |
303 | + background: var(--ja-bg-3); | |
181 | 304 | } |
182 | 305 | |
183 | 306 | .content th { |
184 | 307 | font-weight: 600; |
185 | 308 | text-align: left; |
186 | 309 | padding: 0.75rem 1rem; |
187 | - border-bottom: 2px solid var(--sl-color-border, #e2e8f0); | |
310 | + border-bottom: 2px solid var(--ja-border); | |
311 | + color: var(--ja-accent); | |
188 | 312 | } |
189 | 313 | |
190 | 314 | .content td { |
191 | 315 | padding: 0.75rem 1rem; |
192 | - border-bottom: 1px solid var(--sl-color-border, #e2e8f0); | |
316 | + border-bottom: 1px solid var(--ja-border); | |
193 | 317 | } |
194 | 318 | |
195 | -.content tbody tr:last-child td { | |
196 | - border-bottom: none; | |
319 | +.content tbody tr { | |
320 | + transition: background 0.15s ease; | |
197 | 321 | } |
198 | 322 | |
199 | 323 | .content tbody tr:hover { |
200 | - background: var(--sl-color-bg-hover, #f8fafc); | |
324 | + background: var(--ja-bg-3); | |
201 | 325 | } |
202 | 326 | |
203 | -/* βββ Callouts / Asides βββ */ | |
327 | +/* βββ Callouts βββ */ | |
204 | 328 | .starlight-aside { |
205 | 329 | border-radius: 12px; |
206 | 330 | border-left-width: 4px; |
| @@ -208,35 +332,77 @@ pre code { | ||
208 | 332 | margin: 1.5rem 0; |
209 | 333 | } |
210 | 334 | |
211 | -.starlight-aside--note { | |
212 | - border-left-color: var(--sl-color-blue); | |
213 | - background: rgba(59, 130, 246, 0.05); | |
335 | +.starlight-aside--note { border-left-color: var(--ja-accent-2); background: rgba(0, 212, 255, 0.06); } | |
336 | +.starlight-aside--tip { border-left-color: var(--ja-green); background: rgba(0, 255, 157, 0.06); } | |
337 | +.starlight-aside--caution { border-left-color: #ffb454; background: rgba(255, 180, 84, 0.06); } | |
338 | +.starlight-aside--danger { border-left-color: var(--ja-red); background: rgba(255, 46, 77, 0.06); } | |
339 | + | |
340 | +/* βββ Sci-Fi Background Grid βββ */ | |
341 | +.site-frame::before, | |
342 | +body::before { | |
343 | + content: ''; | |
344 | + position: fixed; | |
345 | + inset: 0; | |
346 | + z-index: -1; | |
347 | + pointer-events: none; | |
348 | + background-image: | |
349 | + linear-gradient(rgba(0, 255, 157, 0.03) 1px, transparent 1px), | |
350 | + linear-gradient(90deg, rgba(0, 255, 157, 0.03) 1px, transparent 1px); | |
351 | + background-size: 48px 48px; | |
352 | + animation: gridScroll 24s linear infinite; | |
353 | +} | |
354 | + | |
355 | +[data-theme='light'] body::before { | |
356 | + background-image: | |
357 | + linear-gradient(rgba(0, 146, 92, 0.04) 1px, transparent 1px), | |
358 | + linear-gradient(90deg, rgba(0, 146, 92, 0.04) 1px, transparent 1px); | |
359 | +} | |
360 | + | |
361 | +@keyframes gridScroll { | |
362 | + from { background-position: 0 0; } | |
363 | + to { background-position: 0 48px; } | |
214 | 364 | } |
215 | 365 | |
216 | -.starlight-aside--tip { | |
217 | - border-left-color: var(--sl-color-green); | |
218 | - background: rgba(16, 185, 129, 0.05); | |
366 | +/* βββ Scanline sweep βββ */ | |
367 | +.main-frame::after, | |
368 | +body::after { | |
369 | + content: ''; | |
370 | + position: fixed; | |
371 | + left: 0; | |
372 | + right: 0; | |
373 | + height: 120px; | |
374 | + z-index: 9999; | |
375 | + pointer-events: none; | |
376 | + background: linear-gradient(180deg, transparent, rgba(0, 255, 157, 0.04), transparent); | |
377 | + animation: scanSweep 7s linear infinite; | |
219 | 378 | } |
220 | 379 | |
221 | -.starlight-aside--caution { | |
222 | - border-left-color: var(--sl-color-yellow); | |
223 | - background: rgba(245, 158, 11, 0.05); | |
380 | +[data-theme='light'] body::after { | |
381 | + background: linear-gradient(180deg, transparent, rgba(0, 146, 92, 0.05), transparent); | |
224 | 382 | } |
225 | 383 | |
226 | -.starlight-aside--danger { | |
227 | - border-left-color: var(--sl-color-red); | |
228 | - background: rgba(239, 68, 68, 0.05); | |
384 | +@keyframes scanSweep { | |
385 | + from { top: -120px; } | |
386 | + to { top: 100vh; } | |
229 | 387 | } |
230 | 388 | |
231 | -/* βββ Hero Gradient βββ */ | |
232 | -.hero-gradient { | |
233 | - background: linear-gradient(135deg, #e6522c 0%, #8b5cf6 50%, #3b82f6 100%); | |
234 | - -webkit-background-clip: text; | |
235 | - -webkit-text-fill-color: transparent; | |
236 | - background-clip: text; | |
389 | +/* βββ Glow Effects βββ */ | |
390 | +.content h2::after { | |
391 | + content: ''; | |
392 | + display: block; | |
393 | + width: 3rem; | |
394 | + height: 3px; | |
395 | + margin-top: 0.5rem; | |
396 | + background: linear-gradient(90deg, var(--ja-accent), transparent); | |
397 | + animation: glowPulse 3s ease-in-out infinite; | |
398 | +} | |
399 | + | |
400 | +@keyframes glowPulse { | |
401 | + 0%, 100% { opacity: 0.6; } | |
402 | + 50% { opacity: 1; box-shadow: 0 0 12px rgba(0, 255, 157, 0.6); } | |
237 | 403 | } |
238 | 404 | |
239 | -/* βββ Feature Grid βββ */ | |
405 | +/* βββ Feature Cards βββ */ | |
240 | 406 | .feature-grid { |
241 | 407 | display: grid; |
242 | 408 | grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); |
| @@ -245,10 +411,11 @@ pre code { | ||
245 | 411 | } |
246 | 412 | |
247 | 413 | .feature-card { |
248 | - border: 1px solid var(--sl-color-border, #e2e8f0); | |
414 | + border: 1px solid var(--ja-border); | |
249 | 415 | border-radius: 16px; |
250 | 416 | padding: 1.75rem; |
251 | - background: var(--sl-color-bg, #ffffff); | |
417 | + background: var(--ja-panel); | |
418 | + backdrop-filter: blur(12px); | |
252 | 419 | transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1); |
253 | 420 | position: relative; |
254 | 421 | overflow: hidden; |
| @@ -261,33 +428,32 @@ pre code { | ||
261 | 428 | left: 0; |
262 | 429 | right: 0; |
263 | 430 | height: 3px; |
264 | - background: linear-gradient(90deg, var(--sl-color-accent), var(--sl-color-blue)); | |
431 | + background: linear-gradient(90deg, var(--ja-accent), var(--ja-accent-2)); | |
265 | 432 | opacity: 0; |
266 | 433 | transition: opacity 0.25s ease; |
267 | 434 | } |
268 | 435 | |
269 | 436 | .feature-card:hover { |
270 | - border-color: var(--sl-color-accent); | |
437 | + border-color: var(--ja-accent); | |
271 | 438 | transform: translateY(-4px); |
272 | - box-shadow: 0 20px 40px -12px rgba(139, 92, 246, 0.15); | |
439 | + box-shadow: 0 0 30px rgba(0, 255, 157, 0.15); | |
273 | 440 | } |
274 | 441 | |
275 | -.feature-card:hover::before { | |
276 | - opacity: 1; | |
277 | -} | |
442 | +.feature-card:hover::before { opacity: 1; } | |
278 | 443 | |
279 | 444 | .feature-card h3 { |
280 | 445 | margin-top: 0; |
281 | 446 | font-size: 1.1rem; |
282 | 447 | font-weight: 600; |
448 | + color: var(--ja-white); | |
283 | 449 | } |
284 | 450 | |
285 | 451 | /* βββ Benchmark Card βββ */ |
286 | 452 | .benchmark-card { |
287 | - border: 1px solid rgba(139, 92, 246, 0.3); | |
453 | + border: 1px solid rgba(0, 255, 157, 0.3); | |
288 | 454 | border-radius: 16px; |
289 | 455 | padding: 1.75rem; |
290 | - background: rgba(17, 24, 39, 0.7); | |
456 | + background: var(--ja-panel); | |
291 | 457 | backdrop-filter: blur(16px); |
292 | 458 | margin-bottom: 1.5rem; |
293 | 459 | position: relative; |
| @@ -301,7 +467,14 @@ pre code { | ||
301 | 467 | left: 0; |
302 | 468 | right: 0; |
303 | 469 | height: 2px; |
304 | - background: linear-gradient(90deg, var(--sl-color-orange), var(--sl-color-accent), var(--sl-color-blue)); | |
470 | + background: linear-gradient(90deg, var(--ja-green), var(--ja-accent), var(--ja-accent-2)); | |
471 | + background-size: 200% 100%; | |
472 | + animation: gradientShift 4s linear infinite; | |
473 | +} | |
474 | + | |
475 | +@keyframes gradientShift { | |
476 | + from { background-position: 0 0; } | |
477 | + to { background-position: 200% 0; } | |
305 | 478 | } |
306 | 479 | |
307 | 480 | /* βββ Performance Table βββ */ |
| @@ -311,14 +484,15 @@ pre code { | ||
311 | 484 | border-spacing: 0; |
312 | 485 | border-radius: 12px; |
313 | 486 | overflow: hidden; |
314 | - border: 1px solid var(--sl-color-border, #e2e8f0); | |
487 | + border: 1px solid var(--ja-border); | |
315 | 488 | } |
316 | 489 | |
317 | 490 | .performance-table th { |
318 | 491 | text-align: left; |
319 | 492 | padding: 1rem 1.25rem; |
320 | 493 | font-weight: 600; |
321 | - background: var(--sl-color-gray-6, #1e293b); | |
494 | + background: var(--ja-bg-3); | |
495 | + color: var(--ja-accent); | |
322 | 496 | } |
323 | 497 | |
324 | 498 | .performance-table td { |
| @@ -332,13 +506,10 @@ pre code { | ||
332 | 506 | } |
333 | 507 | |
334 | 508 | .performance-table .winner { |
335 | - color: var(--sl-color-accent-high); | |
509 | + color: var(--ja-green); | |
336 | 510 | font-weight: 700; |
337 | 511 | font-size: 1.1em; |
338 | -} | |
339 | - | |
340 | -.performance-table tbody tr:hover { | |
341 | - background: var(--sl-color-bg-hover, #f8fafc); | |
512 | + text-shadow: 0 0 10px rgba(0, 255, 157, 0.5); | |
342 | 513 | } |
343 | 514 | |
344 | 515 | /* βββ Sidebar βββ */ |
| @@ -347,179 +518,259 @@ pre code { | ||
347 | 518 | font-size: 0.8rem; |
348 | 519 | text-transform: uppercase; |
349 | 520 | letter-spacing: 0.05em; |
350 | - color: var(--sl-color-text-muted, #64748b); | |
521 | + color: var(--ja-muted); | |
351 | 522 | } |
352 | 523 | |
353 | -/* βββ Badge Styling βββ */ | |
524 | +/* βββ Badges βββ */ | |
354 | 525 | .badge--success { |
355 | - background: rgba(16, 185, 129, 0.15); | |
356 | - color: var(--sl-color-green); | |
357 | - border: 1px solid rgba(16, 185, 129, 0.3); | |
526 | + background: rgba(0, 255, 157, 0.15); | |
527 | + color: var(--ja-green); | |
528 | + border: 1px solid rgba(0, 255, 157, 0.3); | |
358 | 529 | border-radius: 9999px; |
359 | 530 | padding: 0.15em 0.6em; |
360 | 531 | font-size: 0.75rem; |
361 | 532 | font-weight: 600; |
362 | 533 | } |
363 | 534 | |
364 | -/* βββ Card Grid Overrides βββ */ | |
365 | -.sl-card-grid { | |
366 | - gap: 1.25rem; | |
367 | -} | |
368 | - | |
535 | +/* βββ Cards βββ */ | |
369 | 536 | .sl-card { |
370 | 537 | border-radius: 12px; |
371 | - border: 1px solid var(--sl-color-border, #e2e8f0); | |
538 | + border: 1px solid var(--ja-border); | |
372 | 539 | transition: all 0.2s ease; |
540 | + background: var(--ja-panel); | |
373 | 541 | } |
374 | 542 | |
375 | 543 | .sl-card:hover { |
376 | - border-color: var(--sl-color-accent); | |
377 | - box-shadow: 0 8px 24px -8px rgba(139, 92, 246, 0.12); | |
544 | + border-color: var(--ja-accent); | |
545 | + box-shadow: 0 0 24px rgba(0, 255, 157, 0.15); | |
378 | 546 | } |
379 | 547 | |
380 | -/* βββ TOC (Table of Contents) βββ */ | |
381 | -.toc-panel { | |
382 | - font-size: 0.85rem; | |
548 | +/* βββ Search βββ */ | |
549 | +.search-input { | |
550 | + border-radius: 10px !important; | |
551 | + font-size: 0.9rem !important; | |
383 | 552 | } |
384 | 553 | |
385 | -.toc-panel a { | |
386 | - transition: color 0.15s ease; | |
554 | +/* βββ Mobile Responsive βββ */ | |
555 | +@media (max-width: 768px) { | |
556 | + .content h1 { font-size: 1.75rem; } | |
557 | + .content h2 { font-size: 1.3rem; } | |
558 | + .feature-grid { grid-template-columns: 1fr; } | |
559 | + .performance-table { font-size: 0.8rem; } | |
387 | 560 | } |
388 | 561 | |
389 | -/* βββ Breadcrumbs βββ */ | |
390 | -.breadcrumbs a { | |
391 | - font-size: 0.85rem; | |
392 | - color: var(--sl-color-text-muted, #64748b); | |
393 | - transition: color 0.15s ease; | |
562 | +/* βββ Print βββ */ | |
563 | +@media print { | |
564 | + .sidebar, .toc-panel, .edit-link, .prev-next-nav { display: none !important; } | |
565 | + .content { max-width: 100%; } | |
394 | 566 | } |
395 | 567 | |
396 | -.breadcrumbs a:hover { | |
397 | - color: var(--sl-color-accent); | |
568 | +/* βββ Accessibility βββ */ | |
569 | +:focus-visible { | |
570 | + outline: 2px solid var(--ja-accent); | |
571 | + outline-offset: 2px; | |
398 | 572 | } |
399 | 573 | |
400 | -/* βββ Navigation (Prev/Next) βββ */ | |
401 | -.prev-next-nav a { | |
402 | - border-radius: 12px; | |
403 | - transition: all 0.2s ease; | |
404 | - border: 1px solid var(--sl-color-border, #e2e8f0); | |
574 | +::selection { | |
575 | + background: rgba(0, 255, 157, 0.3); | |
576 | + color: var(--ja-white); | |
405 | 577 | } |
406 | 578 | |
407 | -.prev-next-nav a:hover { | |
408 | - border-color: var(--sl-color-accent); | |
409 | - box-shadow: 0 4px 12px -4px rgba(139, 92, 246, 0.15); | |
579 | +[data-theme='light'] ::selection { | |
580 | + background: rgba(0, 146, 92, 0.2); | |
410 | 581 | } |
411 | 582 | |
412 | -/* βββ Search βββ */ | |
413 | -.search-input { | |
414 | - border-radius: 10px !important; | |
415 | - font-size: 0.9rem !important; | |
583 | +/* βββ Scrollbar βββ */ | |
584 | +::-webkit-scrollbar { width: 8px; height: 8px; } | |
585 | +::-webkit-scrollbar-track { background: transparent; } | |
586 | +::-webkit-scrollbar-thumb { | |
587 | + background: var(--ja-border); | |
588 | + border-radius: 4px; | |
416 | 589 | } |
590 | +::-webkit-scrollbar-thumb:hover { background: var(--ja-accent); } | |
417 | 591 | |
418 | -/* βββ Mobile Responsive βββ */ | |
419 | -@media (max-width: 768px) { | |
420 | - .content h1 { | |
421 | - font-size: 1.75rem; | |
422 | - } | |
592 | +/* βββ Animations βββ */ | |
593 | +@keyframes fadeInUp { | |
594 | + from { opacity: 0; transform: translateY(12px); } | |
595 | + to { opacity: 1; transform: translateY(0); } | |
596 | +} | |
423 | 597 | |
424 | - .content h2 { | |
425 | - font-size: 1.3rem; | |
426 | - } | |
598 | +.content > * { | |
599 | + animation: fadeInUp 0.3s ease-out; | |
600 | +} | |
427 | 601 | |
428 | - .feature-grid { | |
429 | - grid-template-columns: 1fr; | |
430 | - } | |
602 | +/* βββ Code Group Tabs βββ */ | |
603 | +.tab-label { | |
604 | + font-family: 'JetBrains Mono', monospace; | |
605 | + font-size: 0.8rem; | |
606 | +} | |
431 | 607 | |
432 | - .performance-table { | |
433 | - font-size: 0.8rem; | |
434 | - } | |
608 | +/* βββ Theme Switcher Widget βββ */ | |
609 | +.ja-theme-switcher { | |
610 | + position: fixed; | |
611 | + bottom: 1.25rem; | |
612 | + right: 1.25rem; | |
613 | + z-index: 1000; | |
614 | + display: flex; | |
615 | + gap: 0.35rem; | |
616 | + padding: 0.35rem; | |
617 | + border-radius: 9999px; | |
618 | + border: 1px solid var(--ja-border); | |
619 | + background: var(--ja-panel); | |
620 | + backdrop-filter: blur(12px); | |
621 | +} | |
435 | 622 | |
436 | - .performance-table th, | |
437 | - .performance-table td { | |
438 | - padding: 0.5rem 0.75rem; | |
439 | - } | |
623 | +.ja-theme-btn { | |
624 | + width: 2rem; | |
625 | + height: 2rem; | |
626 | + border-radius: 9999px; | |
627 | + border: 1px solid transparent; | |
628 | + cursor: pointer; | |
629 | + transition: all 0.2s ease; | |
630 | + position: relative; | |
440 | 631 | } |
441 | 632 | |
442 | -/* βββ Print Styles βββ */ | |
443 | -@media print { | |
444 | - .sidebar, | |
445 | - .toc-panel, | |
446 | - .edit-link, | |
447 | - .prev-next-nav { | |
448 | - display: none !important; | |
449 | - } | |
633 | +.ja-theme-btn:hover { transform: scale(1.15); } | |
634 | +.ja-theme-btn[data-active='true'] { | |
635 | + border-color: var(--ja-white); | |
636 | + box-shadow: 0 0 12px currentColor; | |
637 | +} | |
638 | + | |
639 | +.ja-theme-btn--dark { background: #05080f; color: #00ff9d; } | |
640 | +.ja-theme-btn--light { background: #f4f7fb; color: #00925c; } | |
641 | +.ja-theme-btn--dracula { background: #282a36; color: #bd93f9; } | |
642 | +.ja-theme-btn--rosepine { background: #191724; color: #c4a7e7; } | |
643 | +.ja-theme-btn--warm { background: #1c1412; color: #ff9d5c; } | |
450 | 644 | |
451 | - .content { | |
452 | - max-width: 100%; | |
453 | - } | |
645 | +/* βββ Landing Page βββ */ | |
646 | +.ja-hero { | |
647 | + position: relative; | |
648 | + border: 1px solid var(--ja-border); | |
649 | + border-radius: 24px; | |
650 | + overflow: hidden; | |
651 | + margin: 1.5rem 0; | |
652 | + background: var(--ja-bg-2); | |
454 | 653 | } |
455 | 654 | |
456 | -/* βββ Focus Styles (Accessibility) βββ */ | |
457 | -:focus-visible { | |
458 | - outline: 2px solid var(--sl-color-accent); | |
459 | - outline-offset: 2px; | |
460 | - border-radius: 4px; | |
655 | +.ja-hero img { | |
656 | + display: block; | |
657 | + width: 100%; | |
658 | + height: auto; | |
461 | 659 | } |
462 | 660 | |
463 | -/* βββ Selection βββ */ | |
464 | -::selection { | |
465 | - background: rgba(139, 92, 246, 0.3); | |
466 | - color: var(--sl-color-white); | |
661 | +.ja-stats { | |
662 | + display: grid; | |
663 | + grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); | |
664 | + gap: 1rem; | |
665 | + margin: 2rem 0; | |
467 | 666 | } |
468 | 667 | |
469 | -[data-theme='light'] ::selection { | |
470 | - background: rgba(139, 92, 246, 0.2); | |
471 | - color: var(--sl-color-black); | |
668 | +.ja-stat { | |
669 | + border: 1px solid var(--ja-border); | |
670 | + border-radius: 16px; | |
671 | + padding: 1.25rem; | |
672 | + text-align: center; | |
673 | + background: var(--ja-panel); | |
674 | + transition: all 0.25s ease; | |
472 | 675 | } |
473 | 676 | |
474 | -/* βββ Scrollbar βββ */ | |
475 | -::-webkit-scrollbar { | |
476 | - width: 8px; | |
477 | - height: 8px; | |
677 | +.ja-stat:hover { | |
678 | + border-color: var(--ja-accent); | |
679 | + transform: translateY(-3px); | |
680 | + box-shadow: 0 0 24px rgba(0, 255, 157, 0.12); | |
478 | 681 | } |
479 | 682 | |
480 | -::-webkit-scrollbar-track { | |
481 | - background: transparent; | |
683 | +.ja-stat-value { | |
684 | + font-size: 2rem; | |
685 | + font-weight: 800; | |
686 | + color: var(--ja-accent); | |
687 | + font-family: 'JetBrains Mono', monospace; | |
688 | + text-shadow: 0 0 16px rgba(0, 255, 157, 0.4); | |
482 | 689 | } |
483 | 690 | |
484 | -::-webkit-scrollbar-thumb { | |
485 | - background: var(--sl-color-gray-4, #64748b); | |
486 | - border-radius: 4px; | |
691 | +.ja-stat-label { | |
692 | + font-size: 0.85rem; | |
693 | + color: var(--ja-muted); | |
694 | + margin-top: 0.25rem; | |
487 | 695 | } |
488 | 696 | |
489 | -::-webkit-scrollbar-thumb:hover { | |
490 | - background: var(--sl-color-gray-3, #94a3b8); | |
697 | +/* βββ Landing: Code Terminal βββ */ | |
698 | +.ja-terminal { | |
699 | + border: 1px solid var(--ja-border); | |
700 | + border-radius: 16px; | |
701 | + overflow: hidden; | |
702 | + margin: 2rem 0; | |
703 | + background: var(--ja-bg-2); | |
491 | 704 | } |
492 | 705 | |
493 | -/* βββ Animations βββ */ | |
494 | -@keyframes fadeInUp { | |
495 | - from { | |
496 | - opacity: 0; | |
497 | - transform: translateY(12px); | |
498 | - } | |
499 | - to { | |
500 | - opacity: 1; | |
501 | - transform: translateY(0); | |
502 | - } | |
706 | +.ja-terminal-bar { | |
707 | + display: flex; | |
708 | + align-items: center; | |
709 | + gap: 0.5rem; | |
710 | + padding: 0.6rem 1rem; | |
711 | + background: var(--ja-bg-3); | |
712 | + border-bottom: 1px solid var(--ja-border); | |
503 | 713 | } |
504 | 714 | |
505 | -.content > * { | |
506 | - animation: fadeInUp 0.3s ease-out; | |
715 | +.ja-terminal-dot { | |
716 | + width: 12px; | |
717 | + height: 12px; | |
718 | + border-radius: 9999px; | |
719 | +} | |
720 | + | |
721 | +.ja-terminal-dot:nth-child(1) { background: #ff2e4d; } | |
722 | +.ja-terminal-dot:nth-child(2) { background: #ffb454; } | |
723 | +.ja-terminal-dot:nth-child(3) { background: #00ff9d; } | |
724 | + | |
725 | +.ja-terminal-body { | |
726 | + padding: 1.25rem; | |
727 | + font-family: 'JetBrains Mono', monospace; | |
728 | + font-size: 0.85rem; | |
729 | + color: var(--ja-green); | |
730 | + line-height: 1.7; | |
507 | 731 | } |
508 | 732 | |
509 | -/* βββ Starlight Overrides βββ */ | |
510 | -.starlight-sidebar > div { | |
511 | - border-right: 1px solid var(--sl-color-border, #e2e8f0); | |
733 | +.ja-terminal-body .cm { color: var(--ja-muted); } | |
734 | +.ja-terminal-body .ok { color: var(--ja-green); } | |
735 | +.ja-terminal-body .err { color: var(--ja-red); } | |
736 | + | |
737 | +/* βββ Landing: Feature Matrix Graph β ββ */ | |
738 | +.ja-graph { | |
739 | + border: 1px solid var(--ja-border); | |
740 | + border-radius: 16px; | |
741 | + padding: 1rem; | |
742 | + background: var(--ja-bg-2); | |
743 | + margin: 2rem 0; | |
512 | 744 | } |
513 | 745 | |
514 | -/* βββ Code Group Tabs βββ */ | |
515 | -.tab-label { | |
516 | - font-family: var(--sl-font-mono); | |
517 | - font-size: 0.8rem; | |
746 | +.ja-graph img { | |
747 | + display: block; | |
748 | + width: 100%; | |
749 | + height: auto; | |
750 | +} | |
751 | + | |
752 | +/* βββ Landing: Call-to-Action βββ */ | |
753 | +.ja-cta { | |
754 | + border: 1px solid var(--ja-accent); | |
755 | + border-radius: 16px; | |
756 | + padding: 2rem; | |
757 | + text-align: center; | |
758 | + margin: 2rem 0; | |
759 | + background: | |
760 | + radial-gradient(ellipse at center, rgba(0, 255, 157, 0.08), transparent 70%), | |
761 | + var(--ja-panel); | |
762 | + animation: ctaGlow 4s ease-in-out infinite; | |
763 | +} | |
764 | + | |
765 | +@keyframes ctaGlow { | |
766 | + 0%, 100% { box-shadow: 0 0 20px rgba(0, 255, 157, 0.1); } | |
767 | + 50% { box-shadow: 0 0 40px rgba(0, 255, 157, 0.25); } | |
518 | 768 | } |
519 | 769 | |
520 | -/* βββ Gradient Text Utility βββ */ | |
521 | -.gradient-text { | |
522 | - background: linear-gradient(135deg, #e6522c, #8b5cf6, #3b82f6); | |
770 | +.ja-cta-title { | |
771 | + font-size: 1.75rem; | |
772 | + font-weight: 800; | |
773 | + background: linear-gradient(90deg, var(--ja-white), var(--ja-accent)); | |
523 | 774 | -webkit-background-clip: text; |
524 | 775 | -webkit-text-fill-color: transparent; |
525 | 776 | background-clip: text; |
526 | 777 | |