Kılavuz
CSS-Zero, sözleşme dosyalarına (.css.ts/.css.js) CSS stilleri yazmanıza olanak tanır. derleme zamanında derleyici bu dosyaları çalıştırır, her yardımcı program çağrısını deterministik bir dize jetonuyla değiştirir ve eşleşen CSS'yi tek bir yığın olarak yayınlar. Tarayıcıda hiçbir şey çalışmıyor.
CSS-Zero, tek bir fikir etrafında birleştirilmiş vanilla-extract (tip güvenli `.css.ts' ile sıfır çalışma süresiyle yazma) ve Tailwind CSS'den (birleştirilebilir yardımcı program/varyant sınıfları) ilham almıştır: her şey yeniden kullanılabilir bir simgedir.
Neden "yeniden kullanılabilirlik açısından iyileştirilmiş" bir CSS-in-TS?
Bence kodun değeri genellikle yeniden kullanılabilirliği ile ölçülebilir. CSS-Zero, stilleri karmaşıklık veya katı kısıtlamalar olmadan yeniden kullanılabilir kılma fikri üzerine inşa edilmiştir.
Tek gereklilik, stilleri .css.ts veya .css.js uzantılı dosyalarda yazmaktır. Bu noktadan itibaren, yeniden kullanılabilirlik her düzeyde temel bir kavram olarak ele alınır:
- Yeniden kullanılabilir kurallar —
className,classSelector,id,idSelector,variable,animation,font,layervecontainer; özgürce bir araya getirebileceğiniz küçük ve kendi kendine yeten belirteçler (token'lar) üretir. - Yeniden kullanılabilir varyantlar —
variantsvetheme, tek bir temel stili bir dizi değiştiriciye (modifier) dönüştürür; böylece bir bileşen, CSS tekrarına gerek kalmadan birçok görsel durumu karşılayabilir. - Yeniden kullanılabilir koşullu stiller —
style(config, deps), yalnızca referans verilen belirteçler paket (bundle) içinde fiilen kullanıldığında çıktıya dahil edilen stiller tanımlamanıza olanak tanır. - Yeniden kullanılabilir kütüphaneler — Bir
.css.tsmodülü, aslında sadece standart bir modüldür. Bir stil kütüphanesini NPM'de yayınlayabilir ve diğer herhangi bir dışa aktarılan öğe gibi belirteçleri içe aktararak (import ederek), herhangi bir projede normal bir bağımlılık olarak kullanabilirsiniz. Kütüphane önceden.css.jsformatına da derlenebilir; CSS-Zero her iki formatı da desteklediği için, kütüphaneyi kullanan tarafın dağıtım biçimiyle ilgilenmesi gerekmez.
Her bir yardımcı fonksiyon; düz bir metin ya da diziler veya düz nesneler içinde yer alan metinler döndürür; başka bir şey değil. Derleme aşamasında derleyici, her bir benzersiz sonucu token (ör. o-s_1, --o-v_1) adı verilen özel bir metinle değiştirir ve buna karşılık gelen CSS'i üretir. Token'lar benzersiz olduğu için kullanılmayan stiller "tree-shaking" (gereksiz kodun ayıklanması) işlemiyle elenir; böylece yalnızca gerçekten kullandığınız CSS pakete dahil edilir ve bu yeniden kullanımın çalışma zamanında (runtime) herhangi bir maliyeti olmaz.
Sonuç olarak, stili bir kez tanımlayıp her yerde yeniden kullanabilir ve yalnızca kullandığınız kısımları nihai pakete dahil edebilirsiniz.
Kurulum
npm i @css-zero/core @css-zero/vite-pluginYapılandırma (Vite)
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { cssZero } from '@css-zero/vite-plugin';
export default defineConfig({
plugins: [react(), cssZero()],
});Stilleri bir .css.ts dosyasında yazın ve token'ları bileşenlerinize içe aktarın.
// Button.tsx
import { btn } from './styles.css.ts';
export function Button() {
return <button className={btn}>Click</button>;
}Kamu Hizmetleri
Bireysel kurallar
| Fonksiyon | İmza | Dönüş Değeri |
|---|---|---|
className | (rule?) => string | Benzersiz sınıf belirteci (ör. o-s_1) |
classSelector | (rule?) => [string, string] | [token, '.token'] |
id | (rule?) => string | Benzersiz kimlik belirteci |
idSelector | (rule?) => [string, string] | [token, '#token'] |
variable | (config?) => [string, string] | [--token, 'var(--token)'] |
animation | (config?) => string | Benzersiz @keyframes adı belirteci |
font | (config?) => string | Benzersiz @font-face ailesi belirteci |
layer | () => string | Benzersiz @layer token dizesi |
container | (type?) => [string, string] | [container, '@container name'] |
Birleşik kurallar
| Fonksiyon | İmza | Dönüş Değeri |
|---|---|---|
variants | (config, base?) => Record<string, string> | Değiştirici belirteç eşlemesi, .base.mod seçicisi |
theme | (vars, options) => [Record, Record] | [varRefs, optionClasses] |
style | (config, deps?) => string | Küresel stiller (bağımlılık yok) veya koşullu VE öbekleri |
Örnekler
className
import { className } from '@css-zero/core';
export const btn = className({
color: 'white',
'&:hover': { color: 'gray' },
});
// → 'o-s_1'classSelector
import { classSelector } from '@css-zero/core';
export const [card, cardSelector] = classSelector({
display: 'block',
padding: '16px',
borderRadius: '8px',
});
// card → 'o-s_1', cardSelector → '.o-s_1'id
import { id } from '@css-zero/core';
export const badge = id({ backgroundColor: 'red' });
// → 'o-i_1'idSelector
import { idSelector } from '@css-zero/core';
export const [modal, modalSelector] = idSelector({
position: 'fixed',
inset: 0,
});
// modal → 'o-i_1', modalSelector → '#o-i_1'variable
import { variable } from '@css-zero/core';
// [name, var(name)]
export const [accentKey, accent] = variable('#2b6cb0');
// → ['--o-v_1', 'var(--o-v_1)']animation
import { animation } from '@css-zero/core';
export const spin = animation({
from: { transform: 'rotate(0)' },
to: { transform: 'rotate(360deg)' },
});font
import { font } from '@css-zero/core';
export const inter = font({
src: "url('/fonts/inter.woff2') format('woff2')",
weight: '400 700',
display: 'swap',
});
// → 'o-f_1' — use it as `fontFamily: Inter`layer
import { layer } from '@css-zero/core';
export const base = layer();
// → '@layer o-l_1'container
import { container } from '@css-zero/core';
export const [card, cardQuery] = container('inline-size');
// card → 'o-c_1 / inline-size', cardQuery → '@container o-c_1'variants
import { variants } from '@css-zero/core';
export const tone = variants(
{ primary: { color: '#fff' }, ghost: { color: 'gray' } },
btn // optional base token → selector becomes `.btn.primary`
);
// → { primary: 'o-s_2', ghost: 'o-s_3' }theme
import { theme } from '@css-zero/core';
// [varRefs, optionClasses]
export const [tokens, options] = theme(
{ accent: '#2b6cb0', spacing: '8px' },
{ dark: { accent: '#0f0' }, compact: { spacing: '4px' } }
);
// tokens.accent → 'var(--o-v_1)'
// options.dark → 'o-s_1' (overrides accent when applied)style
import { style } from '@css-zero/core';
// no deps - always emitted
style({ body: { margin: 0 } });
// emitted only if all deps are used in the bundle
style({ [`${btnSelector} > span`]: { display: 'block' } }, [btnSelector]);TypeScript türlerini genişletme
Arayüz bildirimi birleştirme yoluyla kendi özel CSS özelliklerinizi birleştirin:
declare global {
namespace CSSZero {
interface ExtraProperties {
'--brand': string;
}
}
}Stil kitaplığı yayımlama
Sözleşme modülleri (contract modules) sıradan modüllerdir; bu sayede stilleri bir NPM paketi olarak yayınlayabilir ve kullanabilirsiniz:
// your-lib/src/buttons.css.ts
export const [btn, btnSelector] = classSelector({ /* ... */ });
export const btnBg = variants({ primary: { /* ... */ }, ghost: { /* ... */ } }, btn);import { btn, btnBg } from 'your-lib/src/buttons.css';
<button className={`${btn} ${btnBg.primary}`}>Button</button>Eklenti seçenekleri
| Seçenek | Tür | Varsayılan | Açıklama |
|---|---|---|---|
prefix | string | 'o' | Her bir belirteç için isim ön eki (o-s_1, --o-v_1) |
build.inline | boolean | false | CSS'i <link> yerine <head> içindeki bir <style> etiketine gömün |
Önemli
Bir paketleyici (SSR, Jest, tsc/IDE) dışında, @css-zero/core boş dizeler döndürür; imzalar doğrudur ancak değerler yer tutucudur. İşlevsel akış, @css-zero/compiler ve @css-zero/vite-plugin üzerinden ilerler.
Paketler
| Paket | Rol |
|---|---|
@css-zero/core | tsc/IDE için gerçek yardımcı türler + dize şablonları |
@css-zero/compiler | Derleme aracı bağımsız derleme motoru |
@css-zero/vite-plugin | Vite arayüzü: derler, birleştirir, CSS'i satır içine alır veya dışa aktarır, import'ları ayıklar |