Plain CSS
Use plain CSS in Bini.js - import only what you need, where you need it. No framework required.
Plain CSS in Bini.js
Bini.js supports plain .css files natively via Vite. No config needed - just import a .css file and it works. This page covers plain CSS only.
$ npx create-bini-app@latest my-app --none
Or use the interactive prompt and select None under the styling question:
? Select a styling solution:Tailwind CSSCSS Modules> None↑↓ navigate • ⏎ select
Global CSS
For base styles, resets, and utilities that should apply everywhere, import a global stylesheet in your root layout:
/* src/app/globals.css - base reset and variables */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--bg: #ffffff;
--text: #0a0a0a;
--border: #e5e5e5;
}
body {
font-family: system-ui, -apple-system, sans-serif;
background: var(--bg);
color: var(--text);
line-height: 1.6;
}// src/app/layout.tsx - root layout
import './globals.css'
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}CSS for Specific Routes
Import CSS only for the routes that need it. This keeps bundles small and avoids loading unused styles. Each route can have its own stylesheet:
/* src/app/(marketing)/page.css - only loaded for marketing route */
.hero {
min-height: 80vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
}
.hero h1 {
font-size: 3rem;
font-weight: 700;
letter-spacing: -0.02em;
}// src/app/(marketing)/page.tsx
import './page.css'
export default function MarketingPage() {
return (
<div className="hero">
<h1>Welcome to Bini.js</h1>
<p>Build fast, ship faster</p>
</div>
)
}/* src/app/dashboard/page.css - only for dashboard */
.dashboard-grid {
display: grid;
grid-template-columns: 240px 1fr;
gap: 1.5rem;
}
.sidebar {
border-right: 1px solid var(--border);
padding-right: 1.5rem;
}// src/app/dashboard/layout.tsx - layout level CSS for dashboard
import './page.css'
export default function DashboardLayout({ children }: { children: React.ReactNode }) {
return <div className="dashboard-grid">{children}</div>
}/ will not download dashboard/page.css. Import CSS as close as possible to the route that uses it.Blog Layout Example - Scoped CSS
If your blogs layout has a blog.css, that CSS only applies to routes inside the blog folder. Other routes do not get it:
/* src/app/blog/blog.css - only for /blog/* */
.blog-wrapper {
max-width: 720px;
margin: 0 auto;
padding: 2rem 1rem;
line-height: 1.7;
}
.blog-wrapper h1 {
font-size: 2rem;
font-weight: 700;
}
.blog-wrapper article {
color: var(--text);
}// src/app/blog/layout.tsx - import here, scoped to /blog only
import './blog.css'
export default function BlogLayout({ children }: { children: React.ReactNode }) {
return <div className="blog-wrapper">{children}</div>
}/, Vite loads only globals.css + /(marketing)/page.css. When you visit /blog, the blog/layout.tsx chain is loaded, so blog.css is included. If you import blog.css in the root src/app/layout.tsx instead, every route would get it - avoid that for scoped styles.Component CSS
For reusable components, keep a plain .css file next to the component. This still works without CSS Modules - just use clear naming to avoid conflicts:
/* src/app/components/Button.css */
.btn {
padding: 0.5rem 1rem;
border-radius: 0.5rem;
font-weight: 500;
border: 1px solid var(--border);
cursor: pointer;
transition: background 0.2s, color 0.2s;
}
.btn-primary {
background: black;
color: white;
}
.btn-primary:hover {
background: #222;
}// src/app/components/Button.tsx
import './Button.css'
export function Button({ variant = 'primary', children, ...props }: any) {
return <button className={`btn btn-${variant}`} {...props}>{children}</button>
}.module.css) instead. For plain CSS, use BEM or prefixed class names like btn-, card-.External Stylesheets
Import CSS from npm packages only in routes that need them:
// src/app/docs/page.tsx - only docs needs syntax highlighting
import 'prismjs/themes/prism.css'
export default function DocsPage() {
return <article>...</article>
}// src/app/blog/page.tsx - only blog needs markdown styles
import './markdown.css'
export default function BlogPage() {
return <div className="markdown-body">...</div>
}CSS Ordering
CSS is applied in the order you import it. Keep a consistent order:
// layout.tsx - order matters
import './globals.css' // 1. Base reset and variables first
import './theme.css' // 2. Theme and utilities
// Route or component CSS comes after, imported inside page.tsx or component.tsx// src/app/dashboard/page.tsx
import './page.css' // 3. Route-specific CSS - loaded only for this route
export default function DashboardPage() {
return <div className="dashboard">...</div>
}globals.css first, then layout CSS, then route CSS, then component CSS. This avoids specificity surprises.CSS Variables
Use CSS variables for theming - define them once in global CSS, use everywhere:
/* src/app/globals.css */
:root {
--bg: #ffffff;
--text: #0a0a0a;
--border: #e5e5e5;
--radius: 0.5rem;
}
@media (prefers-color-scheme: dark) {
:root {
--bg: #000000;
--text: #fafafa;
--border: #262626;
}
}
body {
background: var(--bg);
color: var(--text);
}Sass/SCSS
Vite supports Sass out of the box. Install and use .scss only where needed:
$ npm install -D sass
/* src/app/dashboard/page.scss - only for dashboard */
.dashboard {
display: grid;
gap: 1rem;
.card {
padding: 1rem;
border: 1px solid var(--border);
border-radius: var(--radius);
&:hover {
border-color: black;
}
}
}import './page.scss' // only dashboard loads this
export default function DashboardPage() {
return <div className="dashboard">...</div>
}CSS-in-JS Alternative
If you prefer CSS-in-JS, use plain CSS setup (--none) and install your library. Only load it in routes that need it:
$ npm install styled-components
// src/app/components/StyledButton.tsx
import styled from 'styled-components'
const Button = styled.button`
padding: 0.5rem 1rem;
border-radius: 0.5rem;
background: black;
color: white;
`
export function StyledButton({ children }: any) {
return <Button>{children}</Button>
}