Skip to content

Troubleshooting

Symptom: Components render but appear completely unstyled.

Solution: Ensure the styles are imported in your app’s entry point, not in individual components:

// ✅ In your main entry file (index.tsx, main.tsx, or layout)
import "@f0rbit/ui/styles";

Symptom: Some styles work but colors or tokens are missing.

Solution: If using granular imports, maintain the correct order:

import "@f0rbit/ui/styles/tokens"; // 1. Variables first
import "@f0rbit/ui/styles/reset"; // 2. Reset second
import "@f0rbit/ui/styles/utilities"; // 3. Utilities third
import "@f0rbit/ui/styles/components"; // 4. Components last

Symptom: Styles work in dev but break in production build.

Solution: Ensure CSS is not being tree-shaken. In vite.config.ts:

export default defineConfig({
optimizeDeps: {
include: ["@f0rbit/ui"],
},
});

Symptom: TypeScript shows “Cannot find module” or missing types.

Solution: Update your tsconfig.json module resolution:

{
"compilerOptions": {
"moduleResolution": "bundler"
}
}

For Node.js 16+ style resolution, use "node16" or "nodenext".

Symptom: JSX element type errors or conflicting React/Solid types.

Solution: Ensure SolidJS is the JSX source:

{
"compilerOptions": {
"jsx": "preserve",
"jsxImportSource": "solid-js"
}
}

Symptom: Component props show as any or lack autocomplete.

Solution: Import prop types explicitly for better inference:

import type { ButtonProps } from "@f0rbit/ui";
const props: ButtonProps = {
variant: "primary", // Now autocompletes
};

Symptom: Components don’t appear, or console shows hydration mismatch errors.

Cause: SolidJS components need client-side JavaScript to be interactive.

Solution for Astro: Add a client: directive to interactive components:

---
import { Button, Modal } from "@f0rbit/ui";
---
<!-- ✅ Will hydrate and be interactive -->
<Button client:load onClick={() => console.log("clicked")}>
Interactive
</Button>
<!-- ❌ Won't respond to events -->
<Button onClick={() => console.log("clicked")}>
Static only
</Button>
DirectiveUse when
client:loadComponent must be interactive immediately
client:idleCan wait until browser is idle
client:visibleOnly needed when scrolled into view

Symptom: Component briefly shows then vanishes.

Cause: Usually a JavaScript error during hydration.

Solution: Check browser console for errors. Common causes:

  • Missing peer dependency (solid-js)
  • Mismatched SolidJS versions
  • Server/client state mismatch

Symptom: Custom CSS doesn’t apply to components.

Cause: CSS specificity or load order issues.

Solution: @f0rbit/ui uses CSS @layer which has lower specificity than regular CSS. Your unlayered styles should win automatically:

/* This will override component styles */
.my-custom-button {
background: red;
}

Symptom: Component styles win over your custom styles.

Solution: Place your styles in a higher-priority layer or outside layers entirely:

/* Option 1: Outside any layer (highest priority) */
.override {
background: red !important; /* Avoid if possible */
}
/* Option 2: Use a later layer */
@layer overrides {
.my-button {
background: red;
}
}

The library uses the f0rbit-ui layer. Structure your layers:

@layer f0rbit-ui, app, overrides;
@layer app {
/* Your app styles */
}
@layer overrides {
/* Specific component overrides */
}

The cleanest way to customize is through CSS variables:

:root {
--accent: oklch(55% 0.2 250);
--radius-md: 0.5rem;
--font-sans: "Inter", system-ui, sans-serif;
}

See Theming for the full list of customizable tokens.


Symptom: Dark mode toggle doesn’t change component appearance.

Cause: The library uses prefers-color-scheme by default.

Solution: To manually control the theme, add a class to the root element:

<html class="dark"> <!-- Forces dark mode -->
<html class="light"> <!-- Forces light mode -->
<html> <!-- Uses system preference -->

In JavaScript:

const setTheme = (theme: "light" | "dark" | "system") => {
document.documentElement.classList.remove("light", "dark");
if (theme !== "system") {
document.documentElement.classList.add(theme);
}
};

Symptom: Page briefly shows wrong theme before correcting.

Cause: Theme is set after page renders.

Solution: Add a blocking script in <head> before any styles:

<head>
<script>
(function() {
const saved = localStorage.getItem("theme");
const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
const theme = saved || (prefersDark ? "dark" : "light");
if (theme === "dark") document.documentElement.classList.add("dark");
})();
</script>
<!-- styles load after -->
</head>

Symptom: Custom colors don’t adapt to dark mode.

Solution: Define both light and dark variants using CSS custom properties:

:root {
--my-color: oklch(95% 0.02 250);
}
:root.dark,
:root:has(html.dark) {
--my-color: oklch(25% 0.02 250);
}
@media (prefers-color-scheme: dark) {
:root:not(.light) {
--my-color: oklch(25% 0.02 250);
}
}

If you’re experiencing a problem not covered here:

  1. Check the GitHub Issues for similar reports
  2. Ensure you’re on the latest version: bun update @f0rbit/ui
  3. Open a new issue with a minimal reproduction