0
# CSS Framework Integration
1
2
Built-in PicoCSS integration with enhanced components and styling utilities for rapid UI development.
3
4
## Capabilities
5
6
### PicoCSS Integration
7
8
FastHTML includes built-in support for PicoCSS, a minimal CSS framework that provides beautiful default styling with minimal markup.
9
10
```python { .api }
11
picocss: str
12
"""PicoCSS CDN URL for including the framework."""
13
14
picolink: list
15
"""PicoCSS link and style elements for HTML head."""
16
17
picocondcss: str
18
"""PicoCSS conditional CDN URL with dark/light theme support."""
19
20
picocondlink: list
21
"""PicoCSS conditional link elements with theme switching."""
22
23
def set_pico_cls():
24
"""Set PicoCSS classes for Jupyter notebook display."""
25
```
26
27
### Enhanced PicoCSS Components
28
29
Pre-built components that leverage PicoCSS styling with additional FastHTML functionality.
30
31
```python { .api }
32
def Card(*c, **kw):
33
"""
34
PicoCSS card component.
35
36
Creates an article element with header and footer sections,
37
styled as a card using PicoCSS classes.
38
39
Args:
40
*c: Card content (header, body, footer elements)
41
**kw: Additional attributes and styling
42
43
Returns:
44
Article element styled as card
45
"""
46
47
def Group(*c, **kw):
48
"""
49
PicoCSS form group.
50
51
Creates a form group container for organizing related
52
form elements with proper spacing and alignment.
53
54
Args:
55
*c: Form elements to group
56
**kw: Additional attributes
57
58
Returns:
59
Div element with form group styling
60
"""
61
62
def Search(*c, **kw):
63
"""
64
PicoCSS search input.
65
66
Creates a search input field with PicoCSS styling
67
and search-specific attributes.
68
69
Args:
70
*c: Content (usually none for input)
71
**kw: Input attributes (placeholder, name, etc.)
72
73
Returns:
74
Search input element
75
"""
76
77
def Grid(*c, **kw):
78
"""
79
PicoCSS grid container.
80
81
Creates a CSS grid container for responsive layouts
82
using PicoCSS grid system.
83
84
Args:
85
*c: Grid items
86
**kw: Grid attributes and styling
87
88
Returns:
89
Div element with grid styling
90
"""
91
92
def DialogX(*c, **kw):
93
"""
94
Enhanced dialog component.
95
96
Creates a modal dialog with PicoCSS styling
97
and enhanced functionality.
98
99
Args:
100
*c: Dialog content
101
**kw: Dialog attributes and options
102
103
Returns:
104
Dialog element with enhanced styling
105
"""
106
107
def Container(*c, **kw):
108
"""
109
PicoCSS container.
110
111
Creates a responsive container with proper max-width
112
and centering using PicoCSS classes.
113
114
Args:
115
*c: Container content
116
**kw: Container attributes
117
118
Returns:
119
Div element with container styling
120
"""
121
122
def PicoBusy(*c, **kw):
123
"""
124
PicoCSS busy indicator.
125
126
Creates a loading spinner or busy indicator
127
using PicoCSS styling.
128
129
Args:
130
*c: Content (usually none)
131
**kw: Styling attributes
132
133
Returns:
134
Element with busy indicator styling
135
"""
136
```
137
138
### Style and Script Enhancement
139
140
Enhanced style and script elements with additional functionality for CSS and JavaScript management.
141
142
```python { .api }
143
def Style(*c, **kw):
144
"""
145
Enhanced style tag.
146
147
Creates HTML style element with enhanced functionality
148
for CSS management and processing.
149
150
Args:
151
*c: CSS content
152
**kw: Style attributes
153
154
Returns:
155
Style element with enhanced functionality
156
"""
157
158
def StyleX(*c, **kw):
159
"""
160
Style tag with CSS file loading.
161
162
Creates style element that can load CSS from external
163
files or include inline styles.
164
165
Args:
166
*c: CSS content or file references
167
**kw: Style attributes and loading options
168
169
Returns:
170
Style element with file loading capability
171
"""
172
173
def Script(*c, **kw):
174
"""
175
Enhanced script tag.
176
177
Creates HTML script element with enhanced functionality
178
for JavaScript management.
179
180
Args:
181
*c: JavaScript content
182
**kw: Script attributes (src, type, etc.)
183
184
Returns:
185
Script element with enhanced functionality
186
"""
187
188
def ScriptX(*c, **kw):
189
"""
190
Script tag with file loading.
191
192
Creates script element that can load JavaScript from
193
external files or include inline scripts.
194
195
Args:
196
*c: JavaScript content or file references
197
**kw: Script attributes and loading options
198
199
Returns:
200
Script element with file loading capability
201
"""
202
```
203
204
### CSS Utility Functions
205
206
Utility functions for CSS processing and variable management.
207
208
```python { .api }
209
def replace_css_vars(css_str: str, variables: dict) -> str:
210
"""
211
Replace CSS variables in string.
212
213
Substitutes CSS custom properties with actual values
214
for dynamic styling.
215
216
Args:
217
css_str: CSS string containing variables
218
variables: Dictionary of variable name/value pairs
219
220
Returns:
221
str: CSS string with variables replaced
222
"""
223
224
def loose_format(template: str, **kwargs) -> str:
225
"""
226
Flexible string formatting for CSS templates.
227
228
Args:
229
template: String template with placeholders
230
**kwargs: Values to substitute
231
232
Returns:
233
str: Formatted string
234
"""
235
236
def double_braces(s: str) -> str:
237
"""
238
Add double braces to string for template processing.
239
240
Args:
241
s: String to process
242
243
Returns:
244
str: String with double braces added
245
"""
246
247
def undouble_braces(s: str) -> str:
248
"""
249
Remove double braces from string.
250
251
Args:
252
s: String with double braces
253
254
Returns:
255
str: String with double braces removed
256
"""
257
```
258
259
### Media Query Utilities
260
261
Functions for handling responsive design and theme switching.
262
263
```python { .api }
264
def light_media() -> str:
265
"""
266
Light mode media query CSS.
267
268
Returns CSS media query for light color scheme preferences.
269
270
Returns:
271
str: CSS media query for light mode
272
"""
273
274
def dark_media() -> str:
275
"""
276
Dark mode media query CSS.
277
278
Returns CSS media query for dark color scheme preferences.
279
280
Returns:
281
str: CSS media query for dark mode
282
"""
283
```
284
285
## Usage Examples
286
287
### Basic PicoCSS Integration
288
289
```python
290
from fasthtml.common import *
291
292
app, rt = fast_app(pico=True) # Enable PicoCSS
293
294
@rt('/')
295
def homepage():
296
return Html(
297
Head(
298
Title("PicoCSS Demo"),
299
Meta(charset="utf-8"),
300
Meta(name="viewport", content="width=device-width, initial-scale=1"),
301
*picolink # Include PicoCSS stylesheets
302
),
303
Body(
304
Container(
305
H1("PicoCSS with FastHTML"),
306
P("This page uses PicoCSS for beautiful default styling."),
307
308
# PicoCSS automatically styles these elements
309
Button("Primary Button", cls="primary"),
310
Button("Secondary Button", cls="secondary"),
311
Button("Contrast Button", cls="contrast"),
312
313
# Form with PicoCSS styling
314
Form(
315
Group(
316
Label("Name:", for_="name"),
317
Input(type="text", name="name", placeholder="Enter your name")
318
),
319
Group(
320
Label("Email:", for_="email"),
321
Input(type="email", name="email", placeholder="Enter your email")
322
),
323
Button("Submit", type="submit")
324
)
325
)
326
)
327
)
328
```
329
330
### Card Components and Layouts
331
332
```python
333
from fasthtml.common import *
334
335
app, rt = fast_app(pico=True)
336
337
@rt('/cards')
338
def card_demo():
339
return Titled("Card Components",
340
Container(
341
H1("PicoCSS Card Examples"),
342
343
Grid(
344
# Basic card
345
Card(
346
Header(H3("Basic Card")),
347
P("This is a basic card with header and content."),
348
Footer(Small("Card footer"))
349
),
350
351
# Card with image
352
Card(
353
Header(
354
Img(src="https://picsum.photos/300/150", alt="Card image")
355
),
356
H4("Image Card"),
357
P("A card with an image header."),
358
Footer(
359
Button("Action", cls="secondary")
360
)
361
),
362
363
# Feature card
364
Card(
365
Header(
366
H3("Feature Card"),
367
P("Premium feature", cls="badge")
368
),
369
Ul(
370
Li("Feature one"),
371
Li("Feature two"),
372
Li("Feature three")
373
),
374
Footer(
375
Button("Get Started", cls="primary"),
376
Button("Learn More", cls="secondary")
377
)
378
),
379
380
style="grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 1rem;"
381
)
382
)
383
)
384
```
385
386
### Forms with PicoCSS Styling
387
388
```python
389
from fasthtml.common import *
390
391
app, rt = fast_app(pico=True)
392
393
@rt('/forms')
394
def form_demo():
395
return Titled("Form Components",
396
Container(
397
H1("PicoCSS Form Styling"),
398
399
# Registration form
400
Card(
401
Header(H2("User Registration")),
402
Form(
403
Grid(
404
Group(
405
Label("First Name:", for_="first_name"),
406
Input(type="text", name="first_name", required=True)
407
),
408
Group(
409
Label("Last Name:", for_="last_name"),
410
Input(type="text", name="last_name", required=True)
411
)
412
),
413
414
Group(
415
Label("Email Address:", for_="email"),
416
Input(type="email", name="email", required=True)
417
),
418
419
Group(
420
Label("Password:", for_="password"),
421
Input(type="password", name="password", required=True)
422
),
423
424
Group(
425
Label("Country:", for_="country"),
426
Select(
427
Option("Select country...", value="", disabled=True, selected=True),
428
Option("United States", value="us"),
429
Option("Canada", value="ca"),
430
Option("United Kingdom", value="uk"),
431
Option("Other", value="other"),
432
name="country"
433
)
434
),
435
436
Group(
437
Label(
438
Input(type="checkbox", name="newsletter", value="yes"),
439
" Subscribe to newsletter"
440
)
441
),
442
443
Group(
444
Button("Create Account", type="submit", cls="primary"),
445
Button("Reset", type="reset", cls="secondary")
446
),
447
448
method="post",
449
action="/register"
450
)
451
),
452
453
# Search form
454
Card(
455
Header(H3("Search")),
456
Form(
457
Group(
458
Search(
459
name="query",
460
placeholder="Search products...",
461
hx_get="/search",
462
hx_target="#search-results",
463
hx_trigger="keyup changed delay:300ms"
464
),
465
Button("Search", type="submit", cls="primary")
466
),
467
Div(id="search-results")
468
)
469
)
470
)
471
)
472
```
473
474
### Responsive Grid Layouts
475
476
```python
477
from fasthtml.common import *
478
479
app, rt = fast_app(pico=True)
480
481
@rt('/layout')
482
def layout_demo():
483
return Titled("Grid Layouts",
484
Container(
485
H1("Responsive Grid Examples"),
486
487
# Two-column layout
488
Section(
489
H2("Two Column Layout"),
490
Grid(
491
Div(
492
H3("Main Content"),
493
P("This is the main content area with more detailed information."),
494
P("Lorem ipsum dolor sit amet, consectetur adipiscing elit.")
495
),
496
Div(
497
H3("Sidebar"),
498
P("This is sidebar content."),
499
Ul(
500
Li("Navigation item 1"),
501
Li("Navigation item 2"),
502
Li("Navigation item 3")
503
)
504
),
505
style="grid-template-columns: 2fr 1fr; gap: 2rem;"
506
)
507
),
508
509
# Three-column layout
510
Section(
511
H2("Three Column Layout"),
512
Grid(
513
Card(
514
Header(H4("Column 1")),
515
P("First column content."),
516
Footer(Button("Action 1"))
517
),
518
Card(
519
Header(H4("Column 2")),
520
P("Second column content."),
521
Footer(Button("Action 2"))
522
),
523
Card(
524
Header(H4("Column 3")),
525
P("Third column content."),
526
Footer(Button("Action 3"))
527
),
528
style="grid-template-columns: repeat(3, 1fr); gap: 1rem;"
529
)
530
),
531
532
# Responsive card grid
533
Section(
534
H2("Responsive Card Grid"),
535
Grid(
536
*[Card(
537
Header(H4(f"Card {i}")),
538
P(f"Content for card {i}"),
539
Footer(Button(f"Action {i}", cls="outline"))
540
) for i in range(1, 9)],
541
style="grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 1rem;"
542
)
543
)
544
)
545
)
546
```
547
548
### Custom Styling and Themes
549
550
```python
551
from fasthtml.common import *
552
553
app, rt = fast_app(pico=True)
554
555
@rt('/custom-style')
556
def custom_styling():
557
# Custom CSS variables
558
custom_styles = Style("""
559
:root {
560
--primary-color: #ff6b6b;
561
--secondary-color: #4ecdc4;
562
--accent-color: #45b7d1;
563
}
564
565
.custom-card {
566
border-left: 4px solid var(--primary-color);
567
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
568
}
569
570
.highlight-button {
571
background: var(--accent-color);
572
color: white;
573
border: none;
574
padding: 0.75rem 1.5rem;
575
border-radius: 0.5rem;
576
transition: all 0.3s ease;
577
}
578
579
.highlight-button:hover {
580
transform: translateY(-2px);
581
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
582
}
583
584
@media (prefers-color-scheme: dark) {
585
.custom-card {
586
background: linear-gradient(135deg, #2c3e50 0%, #34495e 100%);
587
}
588
}
589
""")
590
591
return Html(
592
Head(
593
Title("Custom Styling"),
594
Meta(charset="utf-8"),
595
Meta(name="viewport", content="width=device-width, initial-scale=1"),
596
*picolink,
597
custom_styles
598
),
599
Body(
600
Container(
601
H1("Custom Styling Example"),
602
603
Card(
604
Header(H3("Custom Styled Card")),
605
P("This card uses custom CSS variables and styling."),
606
Footer(
607
Button("Custom Button", cls="highlight-button"),
608
Button("Regular Button", cls="outline")
609
),
610
cls="custom-card"
611
),
612
613
# Theme-aware styling
614
Section(
615
H2("Theme-Aware Components"),
616
P("These components adapt to light/dark mode preferences."),
617
618
Grid(
619
Card(
620
Header("π Light Mode"),
621
P("Optimized for light backgrounds")
622
),
623
Card(
624
Header("π Dark Mode"),
625
P("Automatically adapts to dark mode")
626
),
627
style="grid-template-columns: repeat(2, 1fr); gap: 1rem;"
628
)
629
)
630
)
631
)
632
)
633
```
634
635
### Dynamic Styling with HTMX
636
637
```python
638
from fasthtml.common import *
639
640
app, rt = fast_app(pico=True)
641
642
@rt('/dynamic-style')
643
def dynamic_styling():
644
return Titled("Dynamic Styling",
645
Container(
646
H1("Dynamic Style Changes"),
647
648
# Color theme switcher
649
Card(
650
Header(H3("Theme Switcher")),
651
Group(
652
Button(
653
"Blue Theme",
654
hx_post="/set-theme/blue",
655
hx_target="#themed-content",
656
hx_swap="outerHTML"
657
),
658
Button(
659
"Green Theme",
660
hx_post="/set-theme/green",
661
hx_target="#themed-content",
662
hx_swap="outerHTML"
663
),
664
Button(
665
"Red Theme",
666
hx_post="/set-theme/red",
667
hx_target="#themed-content",
668
hx_swap="outerHTML"
669
)
670
),
671
Div(id="themed-content",
672
P("Click a theme button to change the styling dynamically.")
673
)
674
),
675
676
# Dynamic CSS classes
677
Card(
678
Header(H3("Dynamic Classes")),
679
Button(
680
"Toggle Highlight",
681
hx_post="/toggle-highlight",
682
hx_target="#highlight-demo",
683
hx_swap="outerHTML"
684
),
685
Div(id="highlight-demo",
686
P("This content can be dynamically highlighted.", cls="normal")
687
)
688
)
689
)
690
)
691
692
@rt('/set-theme/{theme}', methods=['POST'])
693
def set_theme(theme: str):
694
theme_styles = {
695
'blue': 'background: #e3f2fd; color: #0d47a1; border: 2px solid #2196f3;',
696
'green': 'background: #e8f5e8; color: #2e7d32; border: 2px solid #4caf50;',
697
'red': 'background: #ffebee; color: #c62828; border: 2px solid #f44336;'
698
}
699
700
style = theme_styles.get(theme, '')
701
702
return Div(
703
P(f"Theme changed to {theme.title()}!"),
704
P("This content now uses dynamic styling."),
705
style=style + ' padding: 1rem; border-radius: 0.5rem; margin-top: 1rem;',
706
id="themed-content"
707
)
708
709
@rt('/toggle-highlight', methods=['POST'])
710
def toggle_highlight():
711
return Div(
712
P("This content is now highlighted!",
713
style="background: yellow; padding: 1rem; border-radius: 0.5rem;"),
714
Button(
715
"Remove Highlight",
716
hx_post="/remove-highlight",
717
hx_target="#highlight-demo",
718
hx_swap="outerHTML"
719
),
720
id="highlight-demo"
721
)
722
723
@rt('/remove-highlight', methods=['POST'])
724
def remove_highlight():
725
return Div(
726
P("Highlight removed.", cls="normal"),
727
Button(
728
"Add Highlight",
729
hx_post="/toggle-highlight",
730
hx_target="#highlight-demo",
731
hx_swap="outerHTML"
732
),
733
id="highlight-demo"
734
)
735
```