Skip to content

Commit 8281439

Browse files
committed
fix: migrate to Tailwind v4 CSS-first config and fix asset loading
- Migrate style/tailwind.css from v3 (@tailwind directives + @apply) to v4 (@import "tailwindcss", @theme tokens, @custom-variant, @source) - Replace .prose @apply rules with raw CSS using color-mix() for opacity - Fix CSS not loading: add resolve_css_href() that reads hash.txt to construct hashed CSS path, matching how HydrationScripts handles JS/WASM - Fix font preload: use r#as/r#type (Rust raw identifiers) instead of as_/type_ which rendered literally in HTML - Fix recursion limit: add #![recursion_limit = "256"] for deep view types - Rename Tailwind v3→v4 utilities: backdrop-blur-sm→xs, outline-none→hidden - Remove tailwind-config-file from Cargo.toml (v4 uses CSS-first config) - Gitignore tailwind.config.js (cargo-leptos auto-generates it; v4 ignores it)
1 parent f449e5b commit 8281439

8 files changed

Lines changed: 145 additions & 78 deletions

File tree

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ dist/
2727
# Leptos build artifacts
2828
/pkg/
2929

30+
# cargo-leptos auto-generates this; Tailwind v4 uses CSS-first config instead
31+
tailwind.config.js
32+
3033
# Rust backup files (rustfmt)
3134
**/*.rs.bk
3235

CLAUDE.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,7 @@ mise run build # Production build (server + WASM)
2828
- `src/bin/prerender.rs` — Cross-platform static HTML pre-renderer (zero extra deps)
2929
- `build.rs` — Markdown pipeline: `content/docs/*.md``content_generated.rs` + `search_index.json` + `routes.txt`
3030
- `content/docs/` — 12 markdown files with YAML frontmatter
31-
- `style/` — Tailwind CSS + animations
32-
- `tailwind.config.js` — Bee-themed color tokens, Leptos `class:` transform
31+
- `style/` — Tailwind v4 CSS-first config + animations (no JS config)
3332

3433
## Commands
3534

@@ -85,7 +84,7 @@ Valid sections: `Basics`, `Usage`, `Internals`, `Integration`, `Reference`
8584
- `cargo check --features ssr` and `cargo check --features hydrate --target wasm32-unknown-unknown` are independent — both must pass
8685
- The `search_index.json` is generated by `build.rs` into `$OUT_DIR` and also copied to `target/site/` for serving
8786
- `routes.txt` manifest is generated by `build.rs` — the prerender binary finds it by walking `target/build/`
88-
- Tailwind v4 warning about JS config is informational — the JS config still works and is needed for the Leptos `class:` transform
87+
- cargo-leptos auto-generates `tailwind.config.js` (gitignored) — Tailwind v4 ignores it since we use CSS-first config in `style/tailwind.css`
8988

9089
## Code Style
9190

Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,6 @@ output-name = "commitbee-web"
8484
site-root = "target/site"
8585
site-pkg-dir = "pkg"
8686
tailwind-input-file = "style/tailwind.css"
87-
tailwind-config-file = "tailwind.config.js"
8887
assets-dir = "public"
8988
site-addr = "127.0.0.1:3000"
9089
reload-port = 3001

src/app.rs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ pub fn App() -> impl IntoView {
2929
}
3030

3131
pub fn shell(options: LeptosOptions) -> impl IntoView {
32+
let pkg_path = &options.site_pkg_dir;
33+
let css_href = resolve_css_href(&options, pkg_path);
34+
3235
view! {
3336
<!DOCTYPE html>
3437
<html lang="en">
@@ -38,7 +41,8 @@ pub fn shell(options: LeptosOptions) -> impl IntoView {
3841
<link rel="icon" href="/images/favicon.svg" type="image/svg+xml"/>
3942
<link rel="alternate icon" href="/images/favicon.ico"/>
4043
<link rel="apple-touch-icon" href="/images/apple-touch-icon.png"/>
41-
<link rel="preload" href="/fonts/Inter-Variable.woff2" as_="font" type_="font/woff2" crossorigin="anonymous"/>
44+
<link rel="preload" href="/fonts/Inter-Variable.woff2" r#as="font" r#type="font/woff2" crossorigin="anonymous"/>
45+
<link rel="stylesheet" href=css_href/>
4246
// Inline theme script: prevents FOUC
4347
<script>{r#"
4448
(function(){
@@ -58,3 +62,24 @@ pub fn shell(options: LeptosOptions) -> impl IntoView {
5862
</html>
5963
}
6064
}
65+
66+
fn resolve_css_href(options: &LeptosOptions, pkg_path: &str) -> String {
67+
let mut css_file = options.output_name.to_string();
68+
if options.hash_files {
69+
let hash_path = std::env::current_exe()
70+
.map(|path| path.parent().map(|p| p.to_path_buf()).unwrap_or_default())
71+
.unwrap_or_default()
72+
.join(options.hash_file.as_ref());
73+
if let Ok(hashes) = std::fs::read_to_string(&hash_path) {
74+
for line in hashes.lines() {
75+
let line = line.trim();
76+
if let Some((file, hash)) = line.split_once(':') {
77+
if file == "css" {
78+
css_file.push_str(&format!(".{}", hash.trim()));
79+
}
80+
}
81+
}
82+
}
83+
}
84+
format!("/{pkg_path}/{css_file}.css")
85+
}

src/components/doc_search.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ pub fn DocSearch() -> impl IntoView {
118118
// Modal overlay
119119
<Show when=move || is_open.get()>
120120
<div
121-
class="fixed inset-0 z-[100] flex items-start justify-center pt-[20vh] bg-bark/50 backdrop-blur-sm"
121+
class="fixed inset-0 z-[100] flex items-start justify-center pt-[20vh] bg-bark/50 backdrop-blur-xs"
122122
on:click=move |_| set_is_open.set(false)
123123
>
124124
<div
@@ -133,7 +133,7 @@ pub fn DocSearch() -> impl IntoView {
133133
<input
134134
type="text"
135135
placeholder="Search documentation..."
136-
class="flex-1 bg-transparent px-3 py-4 text-bark placeholder:text-comb/50 outline-none"
136+
class="flex-1 bg-transparent px-3 py-4 text-bark placeholder:text-comb/50 outline-hidden"
137137
autofocus=true
138138
on:input=move |e| set_query.set(event_target_value(&e))
139139
/>

src/main.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
//
33
// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0
44

5+
#![recursion_limit = "256"]
6+
57
#[cfg(feature = "ssr")]
68
#[tokio::main]
79
async fn main() {

style/tailwind.css

Lines changed: 110 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,35 @@
44
* SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0
55
*/
66

7+
@import "tailwindcss";
8+
@import "./animations.css";
9+
10+
/* Scan Rust source files for utility classes */
11+
@source "../src/**/*.rs";
12+
13+
/* Class-based dark mode (toggle via .dark on <html>) */
14+
@custom-variant dark (&:where(.dark, .dark *));
15+
16+
@theme {
17+
/* Bee-themed color tokens */
18+
--color-honey: var(--honey);
19+
--color-honey-light: var(--honey-light);
20+
--color-honey-dark: var(--honey-dark);
21+
--color-nectar: var(--nectar);
22+
--color-comb: var(--comb);
23+
--color-bark: var(--bark);
24+
--color-pollen: var(--pollen);
25+
--color-surface: var(--surface);
26+
--color-surface-raised: var(--surface-raised);
27+
28+
/* Typography */
29+
--font-sans: "Inter", system-ui, sans-serif;
30+
--font-mono: "JetBrains Mono", ui-monospace, monospace;
31+
32+
/* Max widths */
33+
--container-prose: 75ch;
34+
}
35+
736
@font-face {
837
font-family: "Inter";
938
font-style: normal;
@@ -20,10 +49,6 @@
2049
src: url("/fonts/JetBrainsMono-Regular.woff2") format("woff2");
2150
}
2251

23-
@tailwind base;
24-
@tailwind components;
25-
@tailwind utilities;
26-
2752
@layer base {
2853
:root {
2954
--honey: #f59e0b;
@@ -68,96 +93,152 @@
6893
}
6994

7095
body {
71-
font-family: "Inter", system-ui, sans-serif;
72-
background-color: var(--surface);
73-
color: var(--bark);
96+
font-family: var(--font-sans);
97+
background-color: var(--color-surface);
98+
color: var(--color-bark);
7499
-webkit-font-smoothing: antialiased;
75100
-moz-osx-font-smoothing: grayscale;
76101
}
77102

78103
code,
79104
pre,
80105
kbd {
81-
font-family: "JetBrains Mono", ui-monospace, monospace;
106+
font-family: var(--font-mono);
82107
}
83108
}
84109

85110
@layer components {
86111
.prose {
87-
@apply text-bark leading-relaxed;
112+
color: var(--color-bark);
113+
line-height: 1.625;
88114
}
89115

90116
.prose h1 {
91-
@apply text-3xl font-bold mt-0 mb-6;
117+
font-size: 1.875rem;
118+
line-height: 2.25rem;
119+
font-weight: 700;
120+
margin-top: 0;
121+
margin-bottom: 1.5rem;
92122
}
93123

94124
.prose h2 {
95-
@apply text-2xl font-semibold mt-12 mb-4 pb-2 border-b border-honey/10;
125+
font-size: 1.5rem;
126+
line-height: 2rem;
127+
font-weight: 600;
128+
margin-top: 3rem;
129+
margin-bottom: 1rem;
130+
padding-bottom: 0.5rem;
131+
border-bottom: 1px solid color-mix(in oklch, var(--color-honey) 10%, transparent);
96132
}
97133

98134
.prose h3 {
99-
@apply text-xl font-semibold mt-8 mb-3;
135+
font-size: 1.25rem;
136+
line-height: 1.75rem;
137+
font-weight: 600;
138+
margin-top: 2rem;
139+
margin-bottom: 0.75rem;
100140
}
101141

102142
.prose h4 {
103-
@apply text-lg font-medium mt-6 mb-2;
143+
font-size: 1.125rem;
144+
line-height: 1.75rem;
145+
font-weight: 500;
146+
margin-top: 1.5rem;
147+
margin-bottom: 0.5rem;
104148
}
105149

106150
.prose p {
107-
@apply my-4;
151+
margin-top: 1rem;
152+
margin-bottom: 1rem;
108153
}
109154

110155
.prose a {
111-
@apply text-honey hover:underline;
156+
color: var(--color-honey);
157+
}
158+
.prose a:hover {
159+
text-decoration: underline;
112160
}
113161

114162
.prose ul {
115-
@apply my-4 pl-6 list-disc;
163+
margin-top: 1rem;
164+
margin-bottom: 1rem;
165+
padding-left: 1.5rem;
166+
list-style-type: disc;
116167
}
117168

118169
.prose ol {
119-
@apply my-4 pl-6 list-decimal;
170+
margin-top: 1rem;
171+
margin-bottom: 1rem;
172+
padding-left: 1.5rem;
173+
list-style-type: decimal;
120174
}
121175

122176
.prose li {
123-
@apply my-1;
177+
margin-top: 0.25rem;
178+
margin-bottom: 0.25rem;
124179
}
125180

126181
.prose code:not(pre code) {
127-
@apply rounded bg-pollen px-1.5 py-0.5 text-sm font-mono;
182+
border-radius: 0.25rem;
183+
background-color: var(--color-pollen);
184+
padding-left: 0.375rem;
185+
padding-right: 0.375rem;
186+
padding-top: 0.125rem;
187+
padding-bottom: 0.125rem;
188+
font-size: 0.875rem;
189+
font-family: var(--font-mono);
128190
}
129191

130192
.prose pre {
131-
@apply my-6 rounded-lg bg-pollen p-4 overflow-x-auto;
193+
margin-top: 1.5rem;
194+
margin-bottom: 1.5rem;
195+
border-radius: 0.5rem;
196+
background-color: var(--color-pollen);
197+
padding: 1rem;
198+
overflow-x: auto;
132199
}
133200

134201
.prose blockquote {
135-
@apply my-4 border-l-4 border-honey/30 pl-4 italic text-comb;
202+
margin-top: 1rem;
203+
margin-bottom: 1rem;
204+
border-left: 4px solid color-mix(in oklch, var(--color-honey) 30%, transparent);
205+
padding-left: 1rem;
206+
font-style: italic;
207+
color: var(--color-comb);
136208
}
137209

138210
.prose table {
139-
@apply my-6 w-full border-collapse;
211+
margin-top: 1.5rem;
212+
margin-bottom: 1.5rem;
213+
width: 100%;
214+
border-collapse: collapse;
140215
}
141216

142217
.prose th {
143-
@apply border-b border-honey/20 px-4 py-2 text-left font-semibold;
218+
border-bottom: 1px solid color-mix(in oklch, var(--color-honey) 20%, transparent);
219+
padding: 0.5rem 1rem;
220+
text-align: left;
221+
font-weight: 600;
144222
}
145223

146224
.prose td {
147-
@apply border-b border-honey/5 px-4 py-2;
225+
border-bottom: 1px solid color-mix(in oklch, var(--color-honey) 5%, transparent);
226+
padding: 0.5rem 1rem;
148227
}
149228

150229
.prose hr {
151-
@apply my-8 border-honey/10;
230+
margin-top: 2rem;
231+
margin-bottom: 2rem;
232+
border-color: color-mix(in oklch, var(--color-honey) 10%, transparent);
152233
}
153234

154235
.prose strong {
155-
@apply font-semibold;
236+
font-weight: 600;
156237
}
157238

158239
.prose img {
159-
@apply my-6 rounded-lg;
240+
margin-top: 1.5rem;
241+
margin-bottom: 1.5rem;
242+
border-radius: 0.5rem;
160243
}
161244
}
162-
163-
@import "./animations.css";

tailwind.config.js

Lines changed: 0 additions & 42 deletions
This file was deleted.

0 commit comments

Comments
 (0)