Skip to content

Kılavuz ​

Bu bölümde EffCSS'i kuracağız ve nasıl kullanılacağını göreceğiz.

Kurulum ​

Terminalinize şunu yazın:

sh
# npm
npm i effcss

# pnpm
pnpm add effcss

# yarn
yarn add effcss

CSS 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.

sh
npm i -D csstype

Ardından EffCSS ile birleştirin

ts
// 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:

ts
/**
 * 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:

tsx
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.
tsx
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:

tsx
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:

tsx
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:

tsx
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.
tsx
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:

tsx
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:

tsx
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:

tsx
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.
tsx
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:

tsx
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:

tsx
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:

ts
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:

ts
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:

ts
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:

ts
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:

ts
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:

ts
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:

ts
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:

ts
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:

ts
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 ( className ve attribute kullanı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:

ts
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:

ts
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:

ts
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:

ts
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.

Apache Lisansı 2.0 altında yayınlanmıştır