master-co / css

The CSS Language and Framework
https://css.master.co
MIT License
1.82k stars 41 forks source link

✨ Layers #354

Open 1aron opened 6 months ago

1aron commented 6 months ago

Description

🚧 Under internal discussion

開發 Master UI 第一個 Button 元件後我意識到沒有透過 CSS Layers 管理優先級會導致未預期的樣式覆蓋並使 config.styles 無法支援選取器及媒體查詢。

假設 master.css.js:

export default {
    styles: {
        btn: 'inline-flex p:4x bg:white:focus bg:black:hover h:32@sm'
    }
}

預期重構後的 style.sheet

@layer base, preset, theme, style, utility;
@layer theme {
    :root { --text-strong: 0 0 0 }
    .light { --text-strong: 24 32 48 }
    .light { --text-light: 95 115 149 }
    .dark { --text-strong: 239 238 240 }
    .dark { --text-light: 137 136 138 }
}
@layer style {
    .btn { display: inline-flex }               /* inline-flex */
    .btn { padding: 1rem }                      /* p:4x */
    .btn:hover { background-color: #000000 }    /* bg:black:hover */
    .btn:focus { background-color: #ffffff }    /* bg:white:focus */
    @media (min-width: 834px) {                 /* h:32@sm */
        .btn { height: 2rem; }
    }
}
@layer utility {
    .p\:4x { padding: 1rem }
    .pt\:8x { padding-top: 2rem }
}
/* anonymous layer */
@keyframes fade-in {}

產生的 style.sheet.cssRules

[
    CSSLayerStatementRule,
    CSSLayerBlockRule { name: 'theme' … },
    CSSLayerBlockRule { name: 'style' … },
    CSSLayerBlockRule { name: 'utility' … },
    CSSKeyframesRule { name: 'fade' … },
    CSSKeyframesRule { name: 'flash' … },
]

實作

全新 class LayerRule

現在引入 Layer 概念就必須創建 class Layer 形成多個封閉區來排序各自的 rules

// core.ts
class MasterCSS {
    layerStatementRule = new Rule('layer-statement', [
        { text: '@layer base,preset,theme,style,utility' }
    ], this);
    themeLayer = new Layer('theme', this);
    styleLayer = new SyntaxLayer('style', this);
    utilityLayer = new SyntaxLayer('utility', this);
    keyframeLayer = new AnonymousLayer('keyframe', this);
    sheet = new AnonymousLayer('', this);
    constructor() {
        this.sheet.rules = [
            this.layerStatementRule,
            this.themeLayer,
            this.styleLayer,
            this.utilityLayer,
            this.keyframeLayer
        ]
    }
    get text(): string {
        return this.sheet.text
    }
}
// layer.ts
class Layer {
    native?: CSSLayerBlockRule | CSSStyleSheet
    rules: Rule[] = []
    usages: Record<string, number>
    constructor(
        public name: string,
        public css: MasterCSS
    ) {}
    insert(rule: Rule, index?: number) {}
    delete(rule: Rule) {}
    get text(): string {
        return '@layer ' + this.name + '{' + this.rules.map((eachRule) => eachRule.text).join('') + '}'
    }
}
// anonymous-layer.ts
class AnonymousLayer extends Layer {
    native?: CSSStyleSheet
    rules: (Rule | Layer)[] = []
    constructor(
        public name: string,
        public css: MasterCSS
    ) {}
    get text(): string {
        return this.rules.map((eachRule) => eachRule.text).join('')
    }
}
// syntax-layer.ts
class SyntaxLayer extends Layer {
    native?: CSSLayerBlockRule
    constructor(
        public name: string,
        public css: MasterCSS
    ) {
        super()
    }
    insert(rule: Rule) {}
    delete(rule: Rule) {}
}

重構 Rule

// rule.ts
class Rule {
    constructor(
        public readonly name: string,
        public natives: NativeRule[] = [],
        public css: MasterCSS
    ) {}
    get text(): string {
        return this.natives.map((eachNative) => eachNative.text).join('')
    }
}
// syntax-rule.ts
class SyntaxRule extends Rule {
    constructor(
        public readonly name: string,
        public natives: NativeRule[] = [],
        public css: MasterCSS,
        public readonly RegisteredRule: RegisteredRule,
    ) {
        super(name, layer, natives)
    }
}

@layer theme

@layer style

解決問題