🎨 CSS 写到手抽筋?Stylus 说:‘让我来!’

前言

还在手动重复写 margin: 0; padding: 0;?还在为兼容性疯狂加 -webkit- 前缀?大厂前端早已不用原始 CSS 硬刚了!Stylus 作为一款现代化 CSS 预处理器,让你写样式像写 JavaScript 一样爽快。

Stylus:高效的CSS预处理器

基本特性

Stylus是一种CSS预处理器,提供了许多CSS不具备的高级功能:

stylus 复制代码
// 定义变量
$background_color = rgba(255, 255, 255, 0.95)

.wrapper
  background $background_color
  box-shadow 0 0 0 10px rgba(0, 0, 0, 0.1)

优势与使用场景

  1. 变量支持:避免重复值,便于主题切换
  2. 嵌套规则:更清晰的DOM结构表示
  3. 混合(Mixins) :复用样式块
  4. 函数与运算:动态计算样式值
  5. 简洁语法:可选的花括号、分号和冒号

编译与使用

安装Stylus后,可以通过命令行编译.styl文件:

bash 复制代码
npm install -g stylus
stylus -w common.styl -o common.css
  1. 第一个语句是用来安装stylus的直接运行就好
  2. 第二个语句是你编译common.styl文件时使用的,也就是你写CSS代码时使用的,因为浏览器并不能直接编译.styl文件,所以你要先将.styl文件编译成.css文件,也就是用上面给的那个命令,注意要自己切换成自己的.styl文件名,后面的css名可以随便取一个自己想要的

插件的使用

我们要想使用stylus,除了要全局安装之外还要下载一下下面的这个插件。

我们要先进入插件市场,然后搜索stylus,点击我选择的那个插件点击安装即可

案例实战

先看效果,再上代码,最后在分析考点易错点

效果

下面是我们实现的一个简单的效果界面图

代码

styl 复制代码
$background_color = rgba(255, 255, 255, 0.95)

html 
    box-sizing border-box
    min-height 100vh
    display flex
    flex-direction column
    justify-content center
    align-items center
    text-align center
    background url('http://wes.io/hx9M/oh-la-la.jpg') center no-repeat
    background-size cover

* 
    box-sizing border-box

.wrapper
    padding 20px
    min-width 350px   
    background $background_color
    box-shadow 0 0 0 10px rgba(0, 0, 0, 0.1)
    h2 
        text-align center
        margin 0
        font-weight 200


body 
    color pink

.plates
    margin 0
    padding 0
    text-align left
    list-style: none
    li      
        border-bottom 1px solid rgba(0, 0, 0, 0.2)
        padding 10px 0px 
        display flex
    label 
        flex 1
        cursor pointer
    input
        display none


.add-items
    margin-top 20px
    input 
        padding 10px 
        outline 0
        border 1px solid rgba(0, 0, 0, 0.1)
        

我们可以看到.styl文件不用去写:{}了,而且可以直接层叠样式

当我们运行stylus -w common.styl -o common.css命令时,它会实时的将common.styl文件编译成common.css,你可以根据自己的需求来编写,让我们看看它帮我写好的common.css文件吧

css 复制代码
html {
  box-sizing: border-box;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  text-align: center;
  background: url("http://wes.io/hx9M/oh-la-la.jpg") center no-repeat;
  background-size: cover;
}
* {
  box-sizing: border-box;
}
.wrapper {
  padding: 20px;
  min-width: 350px;
  background: rgba(255,255,255,0.95);
  box-shadow: 0 0 0 10px rgba(0,0,0,0.1);
}
.wrapper h2 {
  text-align: center;
  margin: 0;
  font-weight: 200;
}
body {
  color: #ffc0cb;
}
.plates {
  margin: 0;
  padding: 0;
  text-align: left;
  list-style: none;
}
.plates li {
  border-bottom: 1px solid rgba(0,0,0,0.2);
  padding: 10px 0px;
  display: flex;
}
.plates label {
  flex: 1;
  cursor: pointer;
}
.plates input {
  display: none;
}
.add-items {
  margin-top: 20px;
}
.add-items input {
  padding: 10px;
  outline: 0;
  border: 1px solid rgba(0,0,0,0.1);
}
javascript 复制代码
// 获取DOM元素
// 获取添加项目的表单元素
const addItems = document.querySelector('.add-items');
// 获取显示项目列表的元素
const itemsList = document.querySelector('.plates');
// 从本地存储获取项目数据,如果没有则初始化为空数组
let items = JSON.parse(localStorage.getItem('tapasItems')) || [];

// 添加新项目函数
function addItem(e) {
  // 阻止表单默认提交行为
  e.preventDefault();
  // 获取输入框中的文本值
  const text = this.querySelector('[name=item]').value;
  // 创建新项目对象
  const item = {
    text,        // 项目文本
    done: false  // 完成状态初始为false
  };
  // 将新项目添加到数组中
  items.push(item);
  // 更新列表显示
  populateList(items, itemsList);
  // 将更新后的数组保存到本地存储
  localStorage.setItem('tapasItems', JSON.stringify(items));
  // 重置表单
  this.reset();
}

// 渲染项目列表函数
function populateList(plates = [], platesList) {
  // 使用map方法将数组转换为HTML字符串
  platesList.innerHTML = plates.map((plate, i) => {
    return `
      <li>
        <!-- 复选框,绑定data-index属性用于标识项目索引 -->
        <input type="checkbox" data-index=${i} id="item${i}" ${plate.done ? 'checked' : ''}>
        <!-- 项目文本标签 -->
        <label for="item${i}">${plate.text}</label>
      </li>
    `;
  }).join(''); // 将数组转换为字符串
}

// 切换项目完成状态函数
function toggleDone(e) {
  // 如果点击的不是input元素则直接返回
  if (!e.target.matches('input')) return;
  // 获取被点击元素的data-index属性值
  const el = e.target;
  const index = el.dataset.index;
  // 切换项目的完成状态
  items[index].done = !items[index].done;
  // 更新本地存储
  localStorage.setItem('tapasItems', JSON.stringify(items));
  // 重新渲染列表
  populateList(items, itemsList);
}

// 添加事件监听器
// 表单提交事件 - 添加新项目
addItems.addEventListener('submit', addItem);
// 列表点击事件 - 切换项目完成状态
itemsList.addEventListener('click', toggleDone);

// 初始化加载 - 页面加载时渲染已有项目
populateList(items, itemsList);
html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no, viewport-fit=cover">
    <title>Document</title>
    <link rel="stylesheet" href="./common.css">
</head>
<body>
    <div class="wrapper">
        <h2>Local TAPAS</h2>
        <p>请添加您的TAPAS</p>
        <ul class="plates">
            <li>Loading Tapas ...</li>
        </ul>
        <form action="" class="add-items">
            <input 
                type="text"
                placeholder="Item Name"
                required  <!-- 让输入框变成必须填写 -->
                name="item"
            >
            <input type="submit" value="+ Add Item">
        </form>
    </div>

    <script src="./common.js">

    </script>
</body>
</html>

分析考点易错点

1. Stylus 变量 $background_color

考点

  • Stylus 中变量的定义和使用
  • RGBA 颜色值的表示方法

答案

  • $background_color = rgba(255, 255, 255, 0.95) 定义了一个半透明白色背景变量
  • 在 Stylus 中,变量名可以包含 $ 符号,但不是必须的
  • 可以直接在样式中引用变量,如 background $background_color

易错点

  • 忘记变量名前加 $(虽然 Stylus 允许不加,但加了更清晰)
  • RGBA 值写错格式,如漏掉 alpha 通道或使用错误范围值
  • 变量作用域问题(Stylus 变量有作用域概念)

2. 背景图片设置

考点

  • CSS 背景属性的简写方式
  • background-size: cover 的作用
  • 多背景属性的正确顺序

答案

styl 复制代码
background url('http://wes.io/hx9M/oh-la-la.jpg') center no-repeat
background-size cover

等价于 CSS:

css 复制代码
background-image: url('http://wes.io/hx9M/oh-la-la.jpg');
background-position: center;
background-repeat: no-repeat;
background-size: cover;

易错点

  • 混淆 covercontain 的区别:

    • cover:完全覆盖容器,可能裁剪图片
    • contain:完整显示图片,可能留白
  • 背景图片 URL 未加引号导致错误

  • 多个背景属性顺序错误(简写时有特定顺序要求)

  • 忘记设置 no-repeat 导致图片平铺

3. localStorage 使用

考点

  • localStorage 的 API 使用
  • JSON 序列化与反序列化
  • 数据持久化策略

答案

javascript 复制代码
// 存储数据
localStorage.setItem('tapasItems', JSON.stringify(items));

// 读取数据
let items = JSON.parse(localStorage.getItem('tapasItems')) || [];

易错点

  • 忘记使用 JSON.stringify 直接存储对象,导致存储为 [object Object]
  • 读取时忘记使用 JSON.parse,导致得到的是字符串而非对象
  • 未处理 getItem 返回 null 的情况(代码中使用 || [] 做了默认值处理)
  • 存储大量数据超出 localStorage 容量限制(通常 5MB)
  • 不考虑隐私模式下 localStorage 可能不可用的情况

4. Viewport Meta 标签

考点

  • 响应式设计基础
  • 移动端视口控制
  • 各属性的含义

答案

html 复制代码
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no, viewport-fit=cover">

各属性含义:

  • width=device-width:视口宽度等于设备宽度
  • initial-scale=1:初始缩放比例为1
  • user-scalable=no:禁止用户缩放
  • viewport-fit=cover:覆盖整个屏幕(针对刘海屏设备)

易错点

  • 拼写错误如 user-scalable 写成 user-scalabe
  • 错误理解 initial-scale 的作用
  • 在需要用户缩放功能的场景错误地设置 user-scalable=no
  • 忽略 viewport-fit=cover 导致刘海屏设备显示问题
  • 多个属性间缺少逗号分隔(viewport 内容是用逗号分隔的)
相关推荐
yt948321 分钟前
jquery和CSS3圆形倒计时特效
前端·css3·jquery
teeeeeeemo3 分钟前
CSS3 动画基础与技巧
前端·css·笔记·css3
年纪轻轻就扛不住5 分钟前
CSS3 渐变效果
前端·css·css3
Aisanyi9 分钟前
【鸿蒙开发】使用HMRouter路由的使用
前端·harmonyos
杉木笙14 分钟前
Flutter 代码雨实现(矩阵雨)DLC 多图层
前端·flutter
SouthernWind16 分钟前
Vista AI 演示—— 提示词优化功能
前端·vue.js
林太白17 分钟前
也许看了Electron你会理解Tauri,扩宽你的技术栈
前端·后端·electron
前端的日常20 分钟前
JavaScript 必看!算法 O 系列全攻略
前端
anganing23 分钟前
Web 浏览器预览 Excel 及打印
前端·后端
Chad25 分钟前
Vue3 + vite 首屏优化加载速度
前端