模块化CSS

时间:2024-10-31 07:11:15

 模块化CSS是指把页面分割成不同的组成部分,这些组成部分可以在多种上下文中重复使用,并且相互之间没有依赖关系,当修改其中一部分CSS时,不会对其他部分产生意料之外的影响。

1 基础样式

基础样式是指,在开始写模块化样式之前,需要先配置好环境,每个样式表的开头都要写一些给整个页面使用的通用规则。具有以下特点:

1,基础样式不是模块化的,但它为后面编写模块化样式打好基础。配置完成后,很少会再修改。

2,应该是通用的,只添加那些影响页面大部分或全部内容的样式。

3,不应该使用类名或者Id来匹配元素,应只用标签类型或者偶尔用伪类选择器。

2 模块

每个模块都需要一个独一无二的名称。

模块的选择权由单个类名构成,没有其他规则来约束这些样式仅作用在页面上的某个地方。

<div class="message">消息通知</div>

.message {
  padding: 0.8em 1.2em;
  border-radius: 0.2em;
  border: 1px solid #265559;
  color: #265559;
  background-color: #e0f0f2;
}

模块可以保证视觉一致性,精简代码。

2.1 模块的变体

有时需要特意避免一致,比如上面的消息模块,需要提供成功和失败状态时的消息展示,这两种状态的样式结构上一致,但是在字体及背景的颜色上不同。

用“修饰符”来定义这写不同的状态。 修饰符的写法是:模块名称 + 两个连字符。 例如 message--success 与 message--fail。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div class="flex">
    <div class="message">默认状态</div>
    <div class="separate-10"></div>
    <div class="message message--success">成功状态</div>
    <div class="separate-10"></div>
    <div class="message message--fail">失败状态</div>
</div>
</body>
</html>
<style>
    .message {
        padding: 0.8em 1.2em;
        border-radius: 0.2em;
        border: 1px solid #265559;
        color: #265559;
        background-color: #e0f0f2;
    }

    .message--success {
        color: #2f5926;
        border-color: #2f5926;
        background-color: #cfe8c9;
    }

    .message--fail {
        color: #59262f;
        border-color: #59262f;
        background-color: #e8c9cf;
    }

    .flex {
        display: flex;
    }

    .separate-10 {
        width: 10px;
    }

</style>

图 不同状态下的“消息”模块

2.1.1 不要使用依赖语境的选择器

不要使用基于页面位置的后代选择器来修改模块。否则,会有以下问题:

1)必须考虑这段代码放在哪里(和谁放在一起),后续需要修改样式,很难想起它们放在哪里。

2)提升了选择器优先级。当下次需要修改代码时,需要满足或继续提升优先级。

3)可能需要在其他场景用到这些样式,但是需要为这些样式规则添加新的选择器来匹配新的场景,或完整复制一遍样式。

4)会产生越来越长的选择器。

只能是模块本身可以决定自己的样式表现,其他模块不能进入别的模块内部去修改它。

2.2 多元素模块

一个模块通常由多个元素组成。

子元素命名,模版名称 + 双下划线 + 子元素名称。 这样可以清楚表明元素扮演的角色,属于哪个模块。

避免在模块选择器中使用通用标签签名。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div class="media media--right">
    <img src="../asset/dog.jpeg" alt="" class="media__image"/>
    <div class="media__body">
        <h4>吉娃娃</h4>
        <p>
            这是一只可爱的吉娃娃,个头小小的,脾气大大的。非常可爱的呀!
        </p>
    </div>
</div>
</body>
</html>
<style>
    .media {
        padding: 1.5em;
        background-color: #eee;
        border-radius: 0.5em;
    }

    .media:after {
        content: " ";
        display: block;
        clear: both;
    }
    .media__image {
        float: left;
        max-width: 60px;
        margin-right: 1.5em;
    }
    .media--right > .media__image{
        float: right;
        margin-right: 0;
        margin-left: 1.5em;
    }
    .media__body {
        overflow: auto;
        margin-top: 0;
    }
    .media__body h4 {
        margin-top: 0;
    }
</style>

图 “媒体”模块

3 更大的结构

每个模块应该只做一件事(符合单一职责原则)。

模块之间完全独立,这样使得代码更简洁,不需要理解这个模块之前先搞懂另一个,有助于更加灵活地复用模块。

1, 模块里使用定位:

尽量让需要定位的元素关联到同一个模块内的其他元素。只有这样,把模块放在另一个有定位的容器里时,才不会弄乱样式。  

2, 状态类:

一般以is-或has-开头。例如:is-open,has-error等。

修饰符类

通常是静态的。命名方式与状态类不同。

状态类

用于描述元素在响应用户操作或应用交互时的状态。

表 修饰符类与状态类的区别

3.1 模块命名

1,模块的命名应该有意义,无论使用场景是什么。

2,避免使用简单地描述视觉效果的名称。

3,建议强制使用两个词命名每个模块,可以避免模块指代不明确。

4,不要使用像button--20px这样特别精确的修饰符。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div class="dropdown">
    <button class="dropdown__toggle">下拉组件</button>
    <div class="dropdown__drawer">
        <ul class="menu">
            <li>
                <a href="/">菜单项1</a>
                <a href="/">菜单项2</a>
                <a href="/">菜单项3</a>
                <a href="/">菜单项4</a>
                <a href="/">菜单项5</a>
            </li>
        </ul>
    </div>
</div>
</body>
</html>
<script lang="js">
    (function() {
        const toggle = document.querySelector(".dropdown__toggle")
        toggle.addEventListener('click',(event) => {
            event.preventDefault()
            const parent = event.target.parentNode
            parent.classList.toggle('is-open')
        })
    }())
</script>

<style>
    .dropdown {
        display: inline-block;
        position: relative;
    }
    .dropdown__toggle {
        padding: 0.5em 2em 0.5em 1.5em;
    }
    .dropdown__toggle:after {
        content: "";
        display: block;
        border: solid 0.3em;
        border-color: black transparent transparent;
        position: absolute;
        right: 1em;
        top: 1em;
    }
    .dropdown__drawer {
        display: none;
        position: absolute;
        top: 2.1em;
        left: 0;
        min-width: 100%;
        background-color: #eee;
    }

    .dropdown.is-open .dropdown__toggle:after{
        top: 0.7em;
        border-color: transparent transparent black;
    }

    .dropdown.is-open .dropdown__drawer {
        display: block;
    }

    .menu {
        list-style-type: none;
        margin: 0;
        padding-left: 0;
        border: 1px solid #999;
        font-size: 13px;
    }

    .menu > li + li {
        border-top: 1px solid #999;
    }

    .menu > li > a {
        display: block;
        padding: 0.5em 1.5em;
        background-color: #eee;
        color: #369;
        text-decoration: none;
    }
    .menu > li > a:hover {
        background-color: #fff;
    }
</style>

图 “下拉菜单”模块

4 工具类

需要用一个类来对元素做一件简单明确的事,比如让文字居中、让元素左浮动。

工具类有点像小号的模块,应该专注于某种功能,一般只声明一次。通常吧工具类放在样式表的底部,模块代码的下面。

不要滥用工具类,最多十几个工具类就够用了。

工具类是唯一应该使用important注释的地方,应该优先使用它。