or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

application-routing.mdauthentication.mdcss-styling.mddevelopment-tools.mdform-handling.mdhtml-components.mdhtmx-integration.mdindex.mdjavascript-integration.mdnotifications.mdsvg-components.md

css-styling.mddocs/

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

```