Kılavuz
Bu bölümde EffCSS'i kuracağız ve nasıl kullanılacağını göreceğiz.
Kurulum
Terminalinize şunu yazın:
# npm
npm i effcss
# pnpm
pnpm add effcss
# yarn
yarn add effcssCSS türleri
EffCSS varsayılan olarak esnek bir yapıdadır; stiller herhangi bir nesneyi kabul eder. Katı CSS tür denetimini (otomatik tamamlama ve geçersiz değerlerin doğrulanması) etkinleştirmek için, projenizde global ad alanı genişletmesi (global namespace augmentation) yoluyla EffCSS.Properties tanımını bir kez yapın.
Örneğin, csstype paketini yükleyebilirsiniz.
npm i -D csstypeArdından EffCSS ile birleştirin
// env.d.ts
import type { Properties as CSSProperties } from 'csstype';
declare global {
namespace EffCSS {
interface Properties extends CSSProperties {}
}
}
export {};Artık her stil kuralı, CSS özellikleri için tam TS otomatik tamamlama desteğine sahip.
Kullanım
EffCSS ile stiller oluşturmak için yalnızca ilgili yardımcı fonksiyonları (utility'leri) çağırmanız yeterlidir. Neyse ki sayıları oldukça azdır ve isimlerinden ne işe yaradıkları kolayca anlaşılabilir.
Tüm bu yardımcı fonksiyonlar arasında özellikle classNames ve attributes öne çıkmaktadır. Bunlar, seçicilerin (selectors) nasıl uygulanacağını belirleyen bir TypeScript tipi (sözleşmesi) tanımlamanızı gerektirir. Bu sayede hem stillerin oluşturulma sürecini hem de kullanımlarını kontrol altında tutabilirsiniz. Sözleşme tipi, istenilen derinlikte iç içe geçmiş özelliklere sahip bir nesne yapısındadır:
/**
* Components stylesheet
*/
type Components = {
/**
* Is rounded
*/
rounded: true;
/**
* Height
*/
h: 'full' | 'half';
/**
* Card
*/
card: {
/**
* Card background
*/
bg: 'primary' | 'secondary';
/**
* Is card disabled
*/
disabled: boolean;
};
/**
* Spinner component
*/
spinner: {};
};
/**
* Utils stylesheet
*/
type Utils = {
/**
* Width
*/
w: 's' | 'm' | 'l';
/**
* Spacing
*/
spacing: 0 | 1 | 2;
/**
* Blink animation
*/
blink: true;
};Diğer yardımcı araçlar, türleri argümanlarından türetir. Şimdi her birinin kullanımına daha yakından bakalım.
classNames
className, belirtilen içerikle tek bir CSS kuralı oluşturur ve sınıf seçicisini bir metin dizisi (string) olarak döndürür:
import { className } from 'effcss';
// create
const cls = className({
margin: 'auto',
'&:hover': {
outline: '2px solid black',
'.child': {
background: 'grey'
}
}
});
// apply
export const Component = () => {
return <div className={cls}>
Card
</div>
};lazyClassName (veya className.lazy), className'den şu açılardan ayrılır:
- argüman olarak hem bir nesne hem de bir fonksiyon kabul edebilir;
- seçiciyi çözümleyen bir fonksiyon döndürür;
- döndürülen çözümleyici ilk kez kullanıldığında CSS üretir.
import { lazyClassName } from 'effcss';
// create
const resolveCls = lazyClassName(/* the same or function */);
// or className.lazy(/* the same */)
// the rule has not been created yet
const cls = resolveCls();
// the rule has been created
// apply
export const Component = () => {
return <div className={cls}>
Card
</div>
};classNames, bir stil sayfası oluşturur ve sınıf adlarını türetmek için bir fonksiyon döndürür:
import { classNames } from 'effcss';
// declare
type Card = {
w: 's' | 'm' | 'l';
blur: true;
card: {
variant: 1 | 2;
rounded: true;
};
}
// implement
const card = classNames<Card>((selectors) => {
const {w, card, blur} = selectors;
return {
[w.s]: {
width: '12px'
},
[w.m]: {
width: '24px'
},
[w.l]: {
width: '26px'
},
[blur.true]: {
filter: 'blur(5px)'
},
[card]: {
background: 'white',
border: 'none'
},
[card.variant[1]]: {
width: 'auto',
display: 'block',
padding: '12px',
'&:hover': {
cursor: 'pointer'
}
},
[card.variant[2]]: {
width: 'auto',
display: 'flex',
flexDirection: 'column',
padding: '16px',
'&:hover': {
outline: '2px solid black'
}
},
[card.rounded.true]: {
borderRadius: '1rem'
}
}
});
const cls = card({
card: {
rounded: true
},
w: 's'
});
// apply
export const Component = () => {
return <div className={cls}>
Card
</div>
};lazyClassNames (veya alternatif olarak classNames.lazy), kendisine iletilen fonksiyonu çalıştırması ve seçiciler ilk kez türetildikten sonra bir stil sayfası oluşturması bakımından classNamesten ayrılır:
import { lazyClassNames } from 'effcss';
// declare
type Card = {/* the same */};
// implement
const card = lazyClassNames<Card>(/* the same */);
// or classNames.lazy<Card>(/* the same */);
// the stylesheet has not been created yet
const cls = card({
card: {
rounded: true
},
w: 's'
});
// the stylesheet has been created
// apply
export const Component = () => {
return <div className={cls}>
Card
</div>
};attributes
attribute, belirtilen içeriğe sahip tek bir CSS kuralı oluşturur ve öznitelik seçicisini bir nesne olarak döndürür:
import { attribute } from 'effcss';
// create
const attr = attribute({
margin: 'auto',
'&:hover': {
outline: '2px solid black',
'.child': {
background: 'grey'
}
}
});
// apply
export const Component = () => {
return <div {...attr}>
Card
</div>
};lazyAttribute (veya attribute.lazy), attribute'tan şu açılardan ayrılır:
- argüman olarak hem bir nesne hem de bir fonksiyon kabul edebilir;
- seçiciyi çözümleyen bir fonksiyon döndürür;
- döndürülen çözümleyici ilk kez kullanıldığında CSS üretir.
import { lazyAttribute } from 'effcss';
// create
const resolveAttr = lazyAttribute(/* the same or function */);
// or attribute.lazy(/* the same */)
// the rule has not been created yet
const attr = resolveAttr();
// the rule has been created
// apply
export const Component = () => {
return <div {...attr}>
Card
</div>
};attributes, bir stil sayfası oluşturur ve öznitelikleri türetmek için bir fonksiyon döndürür:
import { attributes } from 'effcss';
// declare
type Card = {
w: 's' | 'm' | 'l';
blur: true;
card: {
variant: 1 | 2;
rounded: true;
};
}
// implement
const card = attributes<Card>((selectors) => {
const {w, card, blur} = selectors;
return {
[w.s]: {
width: '12px'
},
[w.m]: {
width: '24px'
},
[w.l]: {
width: '26px'
},
[blur.true]: {
filter: 'blur(5px)'
},
[card]: {
background: 'white',
border: 'none'
},
[card.variant[1]]: {
width: 'auto',
display: 'block',
padding: '12px',
'&:hover': {
cursor: 'pointer'
}
},
[card.variant[2]]: {
width: 'auto',
display: 'flex',
flexDirection: 'column',
padding: '16px',
'&:hover': {
outline: '2px solid black'
}
},
[card.rounded.true]: {
borderRadius: '1rem'
}
}
});
const attrs = card({
card: {
rounded: true
},
w: 's'
});
// apply
export const Component = () => {
return <div {...attrs}>
Card
</div>
};lazyAttributes (veya alternatif olarak attributes.lazy), seçiciler ilk kez türetildikten sonra kendisine iletilen fonksiyonu çalıştırıp bir stil sayfası oluşturması bakımından attributestan ayrılır:
import { lazyAttributes } from 'effcss';
// declare
type Card = {/* the same */};
// implement
const card = lazyAttributes<Card>(/* the same */);
// or attributes.lazy<Card>(/* the same */);
// the stylesheet has not been created yet
const attrs = card({
card: {
rounded: true
},
w: 's'
});
// the stylesheet has been created
// apply
export const Component = () => {
return <div {...attrs}>
Card
</div>
};id
id, belirtilen içeriğe sahip tek bir CSS kuralı oluşturur ve id seçicisini bir metin dizisi (string) olarak döndürür:
import { id } from 'effcss';
// create
const myId = id({
margin: 'auto',
'&:hover': {
outline: '2px solid black',
'.child': {
background: 'grey'
}
}
});
// apply
export const Component = () => {
return <div id={myId}>
Card
</div>
};lazyId (veya id.lazy), id'den şu açılardan ayrılır:
- argüman olarak hem bir nesne hem de bir fonksiyon kabul edebilir;
- seçiciyi (selector) çözümleyen bir fonksiyon döndürür;
- döndürülen çözümleyici ilk kez kullanıldığında CSS üretir.
import { lazyId } from 'effcss';
// create
const resolveId = lazyId(/* the same or function */);
// or id.lazy(/* the same */)
// the rule has not been created yet
const myId = resolveId();
// the rule has been created
// apply
export const Component = () => {
return <div id={myId}>
Card
</div>
};customStyles
customStyles, türetilmiş seçiciler içermeyen bir stil sayfası oluşturur:
import { customStyles } from 'effcss';
// implement
customStyles(() => ({
'.custom': {
background: 'transparent',
width: '100%',
'&:hover': {
outline: '2px solid black'
}
},
'@media screen and (max-width: 768px)': {
'.custom': {
width: '50%'
}
}
}));
// apply
export const Component = () => {
return <div className='custom'>
Card
</div>
};lazyCustomStyles, kendisine iletilen fonksiyonu çalıştırıp ilk sonuç çağrısından sonra bir stil sayfası oluşturması bakımından customStyles'dan ayrılır:
import { lazyCustomStyles } from 'effcss';
// implement
const applyStyles = lazyCustomStyles(/* the same */);
// the stylesheet has not been created yet
applyStyles();
// the stylesheet has been created
// apply
export const Component = () => {
return <div className='custom'>
Card
</div>
};variables
variable tek bir CSS @property kuralı oluştururken, variables aynı anda birkaç tane oluşturur:
import { customStyles, variable, variables } from 'effcss';
// global
const offset = variable('10px');
const colors = variable({
primary: {
syntax: 'color',
inherits: false,
initialValue: '#2192a7'
},
secondary: '#425158'
});
customStyles(() => {
// local
const localOffset = variable({
inherits: true,
initialValue: '12px'
});
const localColors = variables({
primary: '#2192a7',
secondary: '#425158'
});
return {
'.global': {
background: colors.primary(),
// with fallback value
padding: offset('8px'),
},
'.local': {
// with fallback value
background: localColors.primary('grey'),
padding: localOffset()
},
'.override': {
[localColors.primary]: 'grey'
}
};
});İlgili yöntemleri kullanarak bir global değişkenin başlangıç değerini alabilir ve ayarlayabilirsiniz:
const offset = variable('10px');
const colors = variable({
primary: {
syntax: 'color',
inherits: false,
initialValue: '#2192a7'
},
secondary: '#425158'
});
offset.set('14px');
colors.primary.set('grey');
colors.secondary.set('green');
const actualOffsetValue = offset.get();
const actualPrimaryColorValue = colors.primary.get();INFO
Yerel değişkenlere yönelik set/get metotları birer taslak (stub) niteliğindedir; zira bu değişkenler ayrı bir stil sayfasında oluşturulur ve oluşturucu fonksiyon yürütüldüğü sırada henüz mevcut değildirler. Bu nedenle, söz konusu metotlar yalnızca genel (global) değişkenler üzerinde çağrılabilir.
animations
animation tek bir CSS @keyframes kuralı oluşturur, animations ise aynı anda birkaç tane oluşturur:
import { customStyles, animation, animations } from 'effcss';
// global
const spin = animation({
from: {
transform: 'rotate(0deg)',
},
to: {
transform: 'rotate(360deg)',
},
});
const blink = animations({
simple: {
'50%': {
visibility: 'hidden'
}
},
smooth: {
'0%': {
opacity: 1
},
'50%': {
opacity: 0
},
'100%': {
opacity: 1
}
}
});
customStyles(() => {
// local
const localSpin = animation(/* the same */);
const localBlink = animations(/* the same */);
return {
'.global-spin': {
animation: `${spin} 6s infinite`
},
'.global-blink': {
animation: `${blink.smooth} 2s infinite`
},
'.local-spin': {
animation: `${localSpin} 6s infinite`
},
'.local-blink': {
animation: `${localBlink.smooth} 2s infinite`
},
};
});layers
layer tek bir CSS @layer kuralı oluştururken, layers aynı anda birden fazlasını oluşturur:
import { customStyles, layer, layers } from 'effcss';
// global
const single = layer();
const list = layers(['theme', 'layout', 'utilities']);
customStyles(() => {
// local
const localSingle = layer();
const localList = layers(['theme', 'layout', 'utilities']);
return {
[single]: {
'.global-layer': {
background: 'transparent'
}
},
[list.theme]: {
'.global-layer': {
background: '#425158'
}
},
[localSingle]: {
'.local-layer': {
background: 'white'
}
},
[localList.theme]: {
'.local-layer': {
background: 'grey'
}
}
};
});containers
container tek bir CSS @container kuralı oluşturur, containers aynı anda birkaç tane oluşturur:
import { customStyles, container, containers } from 'effcss';
// global
const single = container();
const multiple = containers({
normal: '',
inline: 'inline-size',
scrollState: 'size scroll-state'
});
customStyles(() => {
// local
const localSingle = container();
const localMultiple = containers({
normal: '',
inline: 'inline-size',
scrollState: 'size scroll-state'
});
return {
'.global-container': {
container: single()
},
[single + ' not scroll-state(scrollable: none)']: {
'.inside-global-container': {
width: '100%'
}
},
'.local-container': {
container: localMultiple.inline()
},
[localMultiple.inline + ' (max-width: 768px)']: {
'.inside-local-container': {
width: '100%'
}
},
};
});fonts
font tek bir CSS @font-face kuralı oluşturur, fonts ise aynı anda birkaç tane oluşturur:
import { customStyles, font, fonts } from 'effcss';
// global
const single = font({
src: `url("https://mdn.github.io/shared-assets/fonts/FiraSans-Regular.woff2")`,
genericName: 'sans-serif'
});
const multiple = fonts({
primary: {
src: `url("/fonts/roboto-regular.woff2") format("woff2"), url("/fonts/roboto-regular.woff") format("woff")`,
weight: 400,
style: 'normal',
display: 'swap'
},
secondary: {
src: `url("https://mdn.github.io/shared-assets/fonts/FiraSans-Regular.woff2")`
}
});
customStyles(() => {
// local
const localSingle = font(/* the same */);
const localMultiple = fonts(/* the same */);
return {
'.global-font': {
fontFamily: single()
},
'.global-primary-font': {
// `single` as fallback
fontFamily: multiple.primary(single)
},
'.local': {
fontFamily: localSingle()
},
'.local-primary-font': {
// `localSingle` as fallback
fontFamily: localMultiple.primary(localSingle)
}
};
});theme
theme, dinamik stil geçişini basitleştirmek amacıyla değişkenleri ve bunların değerlerini tema varyantları halinde gruplandırır:
import { theme } from 'effcss';
const colorTheme = theme({
vars: {
bg: { syntax: 'color', inherits: false, initialValue: '#fff' },
fg: { syntax: 'color', inherits: false, initialValue: '#111' },
accent: { syntax: 'color', inherits: true, initialValue: '#2192a7' }
},
options: {
light: { bg: '#fff', fg: '#111', accent: '#2192a7' },
dark: { bg: '#111', fg: '#eee', accent: '#7fd0ff' }
},
initial: 'light'
});
// get current theme variant
colorTheme.get();
// set next theme variant
colorTheme.set('dark');
// get all theme variants
colorTheme.list;
// use theme vars
colorTheme.vars.bg();update
update, global değişkenin/değişkenlerin başlangıç değerini yeniler:
const offset = variable('10px');
const colors = variable({
primary: {
syntax: 'color',
inherits: false,
initialValue: '#2192a7'
},
secondary: '#425158'
});
update(offset, '14px');
update(colors, {
primary: 'grey',
secondary: 'green'
});Daha açık ve anlaşılır olduğu için, mümkün olan her durumda değişkenin set metodunu kullanın.
stylesheet
stylesheet, oluşturulan stil sayfasını döndürür:
const custom = customStyles(() => {
return {
'.custom': {
padding: '1rem'
},
};
});
// specified stylesheet
const customStylesheet = stylesheet(custom);Ayrıca özel stil sayfaları döndüren yardımcı işlevler de mevcuttur:
layersStylesheet, global katmanlar için bir stil sayfası döndürür,variablesStylesheet, global değişkenler için bir stil sayfası döndürür,animationsStylesheet, global animasyonlar için bir stil sayfası döndürür,fontsStylesheet, global yazı tipleri için bir stil sayfası döndürür,sharedStylesheet, global kurallardan (classNameveattributekullanılarak oluşturulan) oluşan bir stil sayfası döndürür.
configure
configure, ilk stil sayfası oluşturulmadan önce çağrılırsa stil üretimini etkiler:
configure({
// custom prefix for ids
prefix: 'custom',
// disable minification
minify: false,
// emulate server-side mode
emulate: true,
// enable lazy mode
lazy: true
});serialize
serialize, argümanını veya oluşturulan tüm stil sayfalarını bir HTML dizgisine dönüştürür:
const custom = customStyles(() => {
return {
'.custom': {
padding: '1rem'
},
};
});
// specified stylesheet
const customHTML = serialize(custom);
// all created stylesheets
const fullHTML = serialize();serializeMeta, argümanının veya oluşturulan tüm stil sayfalarının meta verilerini bir HTML dizisine serileştirir:
import { classNames } from 'effcss';
type Card = {/* the same */};
const card = classNames<Card>(/* the same */);
// specified stylesheet metadata
const cardMetaHTML = serializeMeta(card);
// all created stylesheets metadata
const fullMetaHTML = serialize();Bu sayede stiller ve meta veriler sunucuda hesaplanıp istemci tarafında yeniden kullanılabilir. Bu, özellikle SSG/SSR için yararlıdır.
subscribe
subscribe, EffCSS stil oluşturma olaylarını dinlemenizi sağlar:
import { subscribe, classNames } from 'effcss';
const events = [];
// start listen to events
const unsubscribe = subscribe((event) => events.push(event));
type Card = {/* the same */};
const card = classNames<Card>(/* the same */);
// stop listen to events
unsubscribe();Bu, hassas kontrol ve hata ayıklama sağlayan bir araçtır; bu nedenle canlı ortam derlemesinde (production build) kullanılmamalıdır.