|
| 1 | +--- |
| 2 | +title: CSS and styling |
| 3 | +use_cases: >- |
| 4 | + styling components, css modules, scoped styles, component styling, design |
| 5 | + system setup, visual customization |
| 6 | +tags: |
| 7 | + - css |
| 8 | + - styling |
| 9 | + - modules |
| 10 | + - components |
| 11 | + - design |
| 12 | + - vite |
| 13 | +version: "2.0" |
| 14 | +description: >- |
| 15 | + Style your SolidStart components with CSS, CSS modules, and other styling |
| 16 | + solutions. Implement scoped styles and design systems. |
| 17 | +--- |
| 18 | + |
| 19 | +SolidStart 2.0 introduces a completely revamped CSS rendering mechanism: |
| 20 | + |
| 21 | +- CSS deduplication during server-side rendering works more consistently across the board. |
| 22 | +- Hot module replacement (HMR) of route CSS is now directly managed by Vite instead of SolidStart. |
| 23 | +- CSS in `lazy`-loaded components is now properly server-side rendered and does not result in flashes of unstyled content. |
| 24 | +- Client-side navigation now mounts new CSS directly via Vite instead of SolidStart. |
| 25 | + - Server HTML responses no longer have to include the whole assets manifest. |
| 26 | +- Client-side navigation no longer unmounts old CSS, more closely following Vite's native behaviour. |
| 27 | + |
| 28 | +## Styling components |
| 29 | + |
| 30 | +You can import CSS using ESM syntax anywhere within the component tree. Styles imported as such, are globally mounted in your app. |
| 31 | + |
| 32 | +```tsx title="Card.tsx" tab="component" |
| 33 | +import "./Card.css"; |
| 34 | + |
| 35 | +const Card = (props) => { |
| 36 | + return ( |
| 37 | + <div class="card"> |
| 38 | + <h1>{props.title}</h1> |
| 39 | + <p>{props.text}</p> |
| 40 | + </div> |
| 41 | + ); |
| 42 | +}; |
| 43 | +``` |
| 44 | + |
| 45 | +```css title="Card.css" tab="component" |
| 46 | +.card { |
| 47 | + background-color: #446b9e; |
| 48 | +} |
| 49 | + |
| 50 | +h1 { |
| 51 | + font-size: 1.5em; |
| 52 | + font-weight: bold; |
| 53 | +} |
| 54 | + |
| 55 | +p { |
| 56 | + font-size: 1em; |
| 57 | + font-weight: normal; |
| 58 | +} |
| 59 | +``` |
| 60 | + |
| 61 | +## Locally scoped styles |
| 62 | + |
| 63 | +SolidStart supports [CSS modules](https://github.com/css-modules/css-modules), allowing you to locally scope the imported CSS. |
| 64 | + |
| 65 | +As is standard with [Vite](https://vitejs.dev/guide/features.html#css-modules), any file ending with `.module.css`, `.module.scss` or `.module.sass` is considered a CSS module. Reference the generated css class names via the imported object, e.g. `styles.card`. |
| 66 | + |
| 67 | +```tsx title="Card.tsx" tab="component" |
| 68 | +import styles from "./Card.module.css"; |
| 69 | + |
| 70 | +const Card = (props) => { |
| 71 | + return ( |
| 72 | + <div class={styles.card}> |
| 73 | + <h1>{props.title}</h1> |
| 74 | + <p>{props.text}</p> |
| 75 | + </div> |
| 76 | + ); |
| 77 | +}; |
| 78 | +``` |
| 79 | + |
| 80 | +```css title="Card.module.css" tab="component" |
| 81 | +.card { |
| 82 | + background-color: #446b9e; |
| 83 | +} |
| 84 | + |
| 85 | +div.card > h1 { |
| 86 | + font-size: 1.5em; |
| 87 | + font-weight: bold; |
| 88 | +} |
| 89 | + |
| 90 | +div.card > p { |
| 91 | + font-size: 1em; |
| 92 | + font-weight: normal; |
| 93 | +} |
| 94 | +``` |
| 95 | + |
| 96 | +## Route-specific global styles |
| 97 | + |
| 98 | +Imported CSS stays in the document when navigating to different routes. Therefore routes with different global styles will overlap each other. There exist several strategies to get around this limitation: |
| 99 | + |
| 100 | +### Apply \:has pseudo-class |
| 101 | + |
| 102 | +If you only have to apply few global CSS rules for one specific route, you can use the CSS [\:has](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Selectors/:has) pseudo-class, to tie the global CSS to the route: |
| 103 | + |
| 104 | +```tsx title="routes/about.tsx" tab="component" |
| 105 | +import "./about.css"; |
| 106 | + |
| 107 | +export default function About() { |
| 108 | + return ( |
| 109 | + <main data-route="about"> |
| 110 | + <h1>About us</h1> |
| 111 | + </main> |
| 112 | + ); |
| 113 | +} |
| 114 | +``` |
| 115 | + |
| 116 | +```css title="routes/about.css" tab="component" |
| 117 | +body:has(main[data-route="about"]) { |
| 118 | + background-color: #446b9e; |
| 119 | + color: white; |
| 120 | +} |
| 121 | +``` |
| 122 | + |
| 123 | +### Import with ?url |
| 124 | + |
| 125 | +If the route depends on complex global CSS (e.g. using Tailwind only in one specific route), optimizing all selectors with `:has` might not always be possible. Instead you can import the CSS file by URL and mount it through JSX: |
| 126 | + |
| 127 | +```tsx title="routes/about.tsx" tab="component" |
| 128 | +import { Link } from "@solidjs/meta"; |
| 129 | +import styleUrl from "./about.css?url"; |
| 130 | + |
| 131 | +export default function About() { |
| 132 | + return ( |
| 133 | + <main> |
| 134 | + <Link rel="stylesheet" href={styleUrl} /> |
| 135 | + <h1>About us</h1> |
| 136 | + </main> |
| 137 | + ); |
| 138 | +} |
| 139 | +``` |
| 140 | + |
| 141 | +```css title="routes/about.css" tab="component" |
| 142 | +@import "tailwindcss"; |
| 143 | +``` |
| 144 | + |
| 145 | +## Lazy loading |
| 146 | + |
| 147 | +[Lazy](https://docs.solidjs.com/reference/component-apis/lazy) loading components with CSS is now fully supported in SolidStart 2.0 and no longer results in flashes of unstyled content (FOUC): |
| 148 | + |
| 149 | +```tsx title="App.tsx" tab="component" |
| 150 | +import { lazy } from "solid-js"; |
| 151 | + |
| 152 | +// Lazy with dynamic import |
| 153 | +const Card = lazy(() => import("./Card.tsx")); |
| 154 | + |
| 155 | +// Lazy with glob import |
| 156 | +const components = import.meta.glob("./Car*.tsx"); |
| 157 | +const Card2 = lazy(Object.values(components)[0]); |
| 158 | + |
| 159 | +const App = (props) => { |
| 160 | + return ( |
| 161 | + <main> |
| 162 | + <Card /> |
| 163 | + <Card2 /> |
| 164 | + </main> |
| 165 | + ); |
| 166 | +}; |
| 167 | +``` |
| 168 | + |
| 169 | +```tsx title="Card.tsx" tab="component" |
| 170 | +import "./Card.css"; |
| 171 | + |
| 172 | +const Card = (props) => { |
| 173 | + return ( |
| 174 | + <div class="card"> |
| 175 | + <h1>{props.title}</h1> |
| 176 | + <p>{props.text}</p> |
| 177 | + </div> |
| 178 | + ); |
| 179 | +}; |
| 180 | +``` |
| 181 | + |
| 182 | +```css title="Card.css" tab="component" |
| 183 | +.card { |
| 184 | + background-color: #446b9e; |
| 185 | +} |
| 186 | + |
| 187 | +h1 { |
| 188 | + font-size: 1.5em; |
| 189 | + font-weight: bold; |
| 190 | +} |
| 191 | + |
| 192 | +p { |
| 193 | + font-size: 1em; |
| 194 | + font-weight: normal; |
| 195 | +} |
| 196 | +``` |
| 197 | + |
| 198 | +## Other ways to style components |
| 199 | + |
| 200 | +SolidStart is built on top of Solid, meaning styling is not limited to CSS. |
| 201 | +To see other ways to style components, see the [styling section in the Solid documentation](/guides/styling-your-components). |
0 commit comments