Начало работы: SolidStart
SSR без вспышки: entry-server.tsx пишет тему из cookie на <html>; разметка компонентов — прямо в Solid JSX (class, как в HTML)
1. Создайте приложение
Проверено с SolidStart 2.0 и solid-js 1.9 (шаблону нужен Node 24+). Скаффолдер не ставит зависимости сам.
npm create solid@latest -- my-app --solidstart --v2 --ts --template basic
cd my-app
npm install
2. Установите пакет
Из пакета нужен только CSS: valyro-ui/styles.css (плюс файл темы, если тема не из двух встроенных). Шрифты — отдельные файлы: тема vercel (по умолчанию в ThemeProvider) использует Geist, поэтому рядом со стилями импортируйте valyro-ui/fonts/geist.css и valyro-ui/fonts/geist-mono.css, иначе текст будет системным шрифтом; glass использует системные шрифты, ей файлы не нужны. npm сам положит react и react-dom в node_modules как peer-зависимости, но в бандл они не попадут, пока вы их не импортируете. Пока пакет не опубликован, установите tarball: npm install ../valyro-ui-0.1.0.tgz.
npm install valyro-ui
3. Подключите стили
Шаблонный app.css глобально стилизует main, h1, p и a и спорит с темой — импортируйте вместо него стили пакета.
import { MetaProvider, Title } from "@solidjs/meta";
import { Router } from "@solidjs/router";
import { FileRoutes } from "@solidjs/start/router";
import { Suspense } from "solid-js";
import "valyro-ui/styles.css";
export default function App() {
return (
<Router
root={props => (
<MetaProvider>
<Title>ValyroUI on SolidStart</Title>
<Suspense>{props.children}</Suspense>
</MetaProvider>
)}
>
<FileRoutes />
</Router>
);
}
4. Тема без вспышки
data-theme (vercel, glass или подключённая отдельным файлом тема) и data-mode (light или dark) на <html> включают тему: от них зависят токены --ui-* и фон страницы. Документ рендерится только на сервере: значения берутся из cookie запроса (только известные), гидрация <html> не затрагивает.
// @refresh reload
import { createHandler, StartServer } from "@solidjs/start/server";
import { getRequestEvent } from "solid-js/web";
// Cookie values are user input: only known values reach the HTML.
function cookie(name: string, allowed: string[], fallback: string) {
const header = getRequestEvent()?.request.headers.get("cookie") ?? "";
const value = header
.split(";")
.map(part => part.trim().split("="))
.find(([key]) => key === name)?.[1];
return value && allowed.includes(value) ? value : fallback;
}
export default createHandler(() => (
<StartServer
document={({ assets, children, scripts }) => (
<html
lang="en"
data-theme={cookie("ui-theme", ["glass", "vercel"], "glass")}
data-mode={cookie("ui-mode", ["light", "dark"], "light")}
>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="/favicon.ico" />
{assets}
</head>
<body>
<div id="app">{children}</div>
{scripts}
</body>
</html>
)}
/>
));
5. Переключатель и разметка
Разметка — ровно то, что рендерят React-компоненты (Button, Badge, TextInput, Alert, Card). Без React работают только визуальные компоненты (список — в гайде HTML + CSS); разметку любого из них показывает пример «Использование» на странице компонента, когда выбран этот фреймворк. В Solid JSX она вставляется почти как есть: уберите только focusable="false" у <svg> — этого атрибута нет в типах Solid, и tsc его не пропустит.
import { createSignal, onMount } from "solid-js";
function save(key: "theme" | "mode", value: string) {
document.documentElement.dataset[key] = value;
document.cookie = `ui-${key}=${value}; path=/; max-age=31536000; samesite=lax`;
}
export default function Home() {
// The server already wrote the cookie values on <html> (entry-server.tsx); pick them up after hydration.
const [theme, setTheme] = createSignal("glass");
const [mode, setMode] = createSignal("light");
onMount(() => {
setTheme(document.documentElement.dataset.theme ?? "glass");
setMode(document.documentElement.dataset.mode ?? "light");
});
return (
<main style={{ display: "grid", "justify-items": "start", gap: "16px", "max-width": "560px", padding: "32px" }}>
<div style={{ display: "flex", gap: "8px" }}>
<button class="ui-button" data-variant="secondary" data-size="md" type="button" onClick={() => save("theme", setTheme(theme() === "glass" ? "vercel" : "glass"))}>
<span class="ui-button__label">Toggle theme</span>
</button>
<button class="ui-button" data-variant="secondary" data-size="md" type="button" onClick={() => save("mode", setMode(mode() === "light" ? "dark" : "light"))}>
<span class="ui-button__label">Toggle mode</span>
</button>
</div>
<button class="ui-button" data-variant="primary" data-size="md" type="button"><span class="ui-button__label">Deploy</span></button>
<span class="ui-badge" data-color="green" data-variant="subtle" data-size="md"><span class="ui-badge__label">Ready</span></span>
<div class="ui-field ui-text-input" data-size="md"><input aria-label="Email" placeholder="[email protected]" type="text" class="ui-field__input ui-text-input__input" /></div>
<div role="status" class="ui-alert" data-variant="info">
<span class="ui-alert__icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" class="ui-icon"><circle cx="12" cy="12" r="10"></circle><path d="M12 16v-4"></path><path d="M12 8h.01"></path></svg></span>
<div class="ui-alert__content"><div class="ui-alert__title">Heads up</div><div class="ui-alert__description">Static markup, no React at runtime.</div></div>
</div>
<div class="ui-card ui-panel" data-variant="default" data-padding="md">
<div class="ui-card__header"><h3 class="ui-card__title">Project</h3><p class="ui-card__description">Production deployment</p></div>
<div class="ui-card__content">Body</div>
<div class="ui-card__footer"><button class="ui-button" data-variant="secondary" data-size="md" type="button"><span class="ui-button__label">Open</span></button></div>
</div>
</main>
);
}
6. Соберите и запустите
В production консоль чистая. В npm run dev SolidStart 2.0.6 сам по себе (и на чистом шаблоне) пишет в консоль SyntaxError … resolve-uri.umd.js does not provide an export named 'default'; лечится строкой optimizeDeps: { include: ["@solidjs/start > @jridgewell/trace-mapping > @jridgewell/resolve-uri"] } в vite.config.ts.
npm run build
npm start