# 🚀 用Stylus + Flexbox:打造令人惊艳的现代网页(附源码)

152 阅读8分钟

🚀 用Stylus + Flexbox:打造令人惊艳的现代网页(附源码)

🌟 请看效果图:

20251029-165945.gif

告别繁琐CSS,开启高效样式编写新纪元

🌟 开篇:当CSS遇见编程思维

想象一下这样的场景:

  • 你是否曾因修改主题色而在几十个文件中来回切换?
  • 是否被复杂的布局代码折磨得头晕眼花?
  • 是否渴望像写JavaScript一样优雅地编写样式?

今天,让我们一起打破常规! 我将带你领略Stylus与Flexbox的完美结合,让你以前所未有的效率和优雅,打造出令人惊艳的响应式网页!

🎨 第一部分:Stylus - CSS的华丽蜕变

💫 重新定义样式编写

Stylus不是简单的工具,而是思维方式的升级:

  • 🎯 极简语法:像写诗一样写样式,告别{ } ; 的束缚
  • 🎨 设计系统化:用变量和函数构建可维护的设计体系
  • 🔥 智能高效:嵌套、混合让代码复用变得轻而易举

🚀 三分钟快速上手

体验从传统到现代的飞跃:

  • 安装即用:一行命令开启全新世界
    • 安装stylus插件,在vscode搜索框里下载stylus插件

屏幕截图 2025-10-29 211159.png

  • npm i -g stylus 全局安装stylus包

屏幕截图 2025-10-28 191952.png

  • 🔄 实时编译:代码变化即时呈现,开发体验满分
    • stylus style.styl -o style.css -w输入这个命令,输出compiled style.csswatching style.styl说明运行成功,进入监听模式,就可以实时的把.styl文件转换成.css文件,让浏览器解析。

屏幕截图 2025-10-28 193630.png

  • 🎪 玩法多样:支持多种编写风格,总有一款适合你

🏗️ 第二部分:Flexbox - 布局艺术的革命

💡 重新思考网页布局

Flexbox带来的不仅是技术,更是布局哲学:

  • 🧩 直觉式布局:告别float和position的魔法数字
  • 📱 移动端首选:天然适配多设备场景
  • 🎭 弹性思维:让元素在容器中自由呼吸

🌈 五大核心应用场景

  1. ✨ 完美居中 - 一行代码实现水平垂直居中
    • 在弹性布局里设置了两个自带的居中方法
      • justify-content: center水平居中,主轴对齐
      • align-items: cente,垂直居中,侧轴对齐
body    
    display: flex
    justify-content: center//水平居中
    align-items: center//垂直居中
    height: 100vh
    flex-direction: column
    overflow: hidden//超出就隐藏
  • 效果如图,先将元素水平移到中间,在将元素垂直移到中间

屏幕截图 2025-10-29 000049.png

  1. 📊 等分布局 - 智能分配空间,无需复杂计算
    • flex:将图片等分布局,其核心作用是让所有设置了 flex: 1 的子元素平均分配父容器的可用空间,具体拆解如下:
    • 所有图片容器平均分配父容器的宽度(或高度,取决于 flex-direction:row/cloums :父容器.container总宽度为 90vw,有 5 个 .panel,则每个 .panel 初始宽度为 90vw / 5 = 18vw(忽略边距 margin 的话)。
    • 空间动态分配:当父容器尺寸变化(如窗口缩放),可用空间会自动重新分配,每个 .panel 始终保持相同的比例。
    • flex: 1 通过统一子元素的「拉伸 / 收缩比例」和「初始基准尺寸」,实现了弹性容器内子元素的等比例空间分配,让图片容器在父容器中始终保持均匀分布。
  .container//父元素
    display flex
    justify-content flex-start
    flex-direction row
    width 90vw

    .panel//.container的子元素
        height 50vh
        border-radius 50px
        color #9e3333
        // cursor pointer
        // flex 1
        margin 10px
        position relative
        background-size cover
        background-position center
        transition all 700ms ease-in

1.PNG

2.PNG 4. 🎯 顺序控制 - 自由调整元素显示顺序

  • .panel为例: .container 是 .panel 的父容器,其所有子元素的 order 属性初始默认值均为 0。我们可以通过调整子元素的 order 值,来改变它们在布局中的排列顺序——数值越小的元素将排在越前面,所有子元素会按照 order 值从小到大的顺序重新排列
  • 当我把第一个子元素的 order 值设为 2,而其他子元素未设置(默认为 0),排序后,由于 0 小于 2,第一个元素便会排到所有默认元素之后,具体效果见下图
    .container
    display: flex
    justify-content: flex-start
    flex-direction: columns 
    width 90 vw
        .panel:nth-child(1)
        order: 2
  • 改变nth-child(1)order值之前:

屏幕截图 2025-10-29 232203.png

  • 改变nth-child(1)order值之后:

屏幕截图 2025-10-29 231435.png 5. 📐 对齐艺术 - 精细控制每个元素的位置

🎪 第三部分:交互体验的魔法时刻

⚡ 动态交互新体验

当静态样式遇见动态交互:

  • 🎮 状态管理:用类名切换实现复杂交互效果
  • 🌊 流畅动画:精心设计的过渡让用户体验升级
  • 🎯 用户引导:通过视觉变化引导用户操作

🔗 JavaScript无缝集成

样式与逻辑的完美共舞:

🔥 交互逻辑深度解析:如何用少量JS实现精准的流向控制

各位掘友,在之前的分享中我们已经搭建了完美的CSS架构,今天重点聊聊 JavaScript 如何与 CSS 无缝配合,实现精致的交互效果

💡 设计思路:最小化 JS,最大化效果

我的核心理念是:让 CSS 负责表现,让 JS 只做最关键的状态管理。下面这段代码虽然简短,但蕴含了不少设计巧思。

🎯 核心代码解析

const panels = document.querySelectorAll('.panel')

panels.forEach(function(panel){
    panel.addEventListener('mouseover',function(){
        const cur = document.querySelector('.active');
        if(cur){
            cur.classList.remove('active');
        }
        panel.classList.add('active')
    })
})

🔍 技术细节深度剖析

1. 精准的 DOM 元素获取

document.querySelectorAll('.panel') 这里我特意使用了类选择器而非标签选择器,为什么?

设计考量

  • 🎯 更好的语义化.panel 明确表达了这是一个 UI 组件
  • 🔧 更高的可维护性:未来如果面板元素类型变化,无需修改 JS
  • 📦 更强的复用性:任何元素只要添加 .panel 类就能获得相同交互

2. 事件委托的取舍

很多掘友可能会问:为什么不用事件委托?这里我做了 刻意选择

// 为什么不这样写?
container.addEventListener('mouseover', function(e) {
    if(e.target.classList.contains('panel')) {
        // ...逻辑处理
    }
})

我的考量

  • 性能平衡:面板数量固定且不多,直接绑定不会造成性能问题
  • 🧩 逻辑清晰:每个面板独立管理自己的状态,符合组件化思想
  • 🎪 避免事件冒泡的复杂性:减少 e.targete.currentTarget 的混淆

3. 状态管理的精妙设计

核心状态切换逻辑:

const cur = document.querySelector('.active');
if(cur){
    cur.classList.remove('active');
}
panel.classList.add('active')

为什么这样设计?

  • 🔄 原子性操作:先查询 → 再清除 → 最后设置,确保状态切换的完整性
  • 🚀 性能优化:只在需要时才查询当前激活项,避免维护额外的状态变量
  • 🎨 CSS 驱动:所有动画效果都在 CSS 中定义,JS 只负责状态切换,符合关注点分离原则

🚀 实际开发中的演进思考

第一版:基础功能

// 最简单的实现
panels.forEach(panel => {
    panel.addEventListener('mouseover', () => {
        panels.forEach(p => p.classList.remove('active'));
        panel.classList.add('active');
    });
});

第二版:性能优化

// 减少不必要的 DOM 操作
panels.forEach(panel => {
    panel.addEventListener('mouseover', () => {
        const activePanel = document.querySelector('.panel.active');
        if(activePanel && activePanel !== panel) {
            activePanel.classList.remove('active');
        }
        panel.classList.add('active');
    });
});

最终版:代码健壮性

// 添加边界条件检查
panels.forEach(panel => {
    panel.addEventListener('mouseover', function() {
        const cur = document.querySelector('.active');
        // 避免重复激活
        if(cur === this) return;
        
        if(cur){
            cur.classList.remove('active');
        }
        this.classList.add('active');
    });
});

💡 经验总结

1. CSS-in-JS 的另一种理解

我们不需要复杂的 CSS-in-JS 库,通过 classList API 就能实现样式与逻辑的完美结合。

2. 渐进增强的实践

即使 JavaScript 加载失败或执行出错,基础的 CSS 悬停效果仍然可用,这是真正的渐进增强。

3. 性能与可读性的平衡

在代码可读性和性能之间找到平衡点,避免过度优化。

🎯 进一步优化的思考

如果项目需要更复杂的交互,我会考虑:

  1. 加入触摸事件支持touchstart 移动端适配
  2. 键盘导航:方向键切换面板
  3. 动画生命周期:在动画开始/结束时执行特定逻辑

🤔 互动讨论

掘友们,你们在项目中是如何处理类似交互的?有没有更好的实现思路?欢迎在评论区交流讨论!


如果觉得这篇文章对你有帮助,请点赞 👍 收藏 ⭐️ 关注 👏 三连支持一下!

你的支持是我持续分享的动力!

记住:优秀的开发者不是写更多代码,而是用更优雅的方式解决问题。 Stylys和Flexbox就是你通往优雅开发之路的通行证!


准备好了吗? 让我们开始这段奇妙的旅程,一起探索现代前端开发的无限可能! 🎊

  • 源码
const panels = document.querySelectorAll('.panel')
// console.log(panels,
//     panels[0],
//     typeof panels[0],
//     Object.prototype.toString.call(panels[0])
// )
panels.forEach(function(panel){
    //事件监听需要在具体元素上
    panel.addEventListener('mouseover',function(){
        //点击当前项,添加active类名
        const cur = document.querySelector('.active');
        if(cur){
            cur.classList.remove('active');
        }
        panel.classList.add('active')
    })})
*
    margin 0
    padding  0
.inner
    color #000
body    
    display: flex
    justify-content: center//水平居中
    align-items: center//垂直居中
    height: 100vh
    flex-direction: column
    overflow: hidden//超出就隐藏
.container
    display: flex
    justify-content: flex-start
    flex-direction: columns 
    width 90 vw
    .panel
        height 50vh
        border-radius: 50px
        color#fff
        // cursor pointer
        flex 1
        margin 10px
        position relative
        background-size: cover
        background-position: center
        transition: all 700ms ease-in
        h3 
            font-size 24px
            position absolute//绝对定位
            left: 20px
            right: 20px
            bottom: 0
            opacity 0
            transition: opacity  300ms ease-in 400ms
        &.active
            flex: 6
            h3
                opacity 1
    // .panel:nth-child(1)
    //     order: 2
@media(max-width: 500px){
    .container{
        width 100vw
    }
    .panel:nth-of-type(4),
    .panel:nth-of-type(5){
        display: none
        }
    }
* {
  margin: 0;
  padding: 0;
}
.inner {
  color: #000;
}
body {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
  flex-direction: column;
  overflow: hidden;
}
.container {
  display: flex;
  justify-content: flex-start;
  flex-direction: columns;
  width: 90vw;
}
.container .panel {
  height: 50vh;
  border-radius: 50px;
  color: #fff;
  flex: 1;
  margin: 10px;
  position: relative;
  background-size: cover;
  background-position: center;
  transition: all 700ms ease-in;
}
.container .panel h3 {
  font-size: 24px;
  position: absolute;
  left: 20px;
  right: 20px;
  bottom: 0;
  opacity: 0;
  transition: opacity 300ms ease-in 400ms;
}
.container .panel.active {
  flex: 6;
}
.container .panel.active h3 {
  opacity: 1;
}
@media (max-width: 500px) {
  .container {
    width: 100vw;
  }
  .panel:nth-of-type(4),
  .panel:nth-of-type(5) {
    display: none;
  }
}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>CARDS</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    

    <div class="container">
      <div class="panel active" style="background-image: url('https://images.unsplash.com/photo-1558979158-65a1eaa08691?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1350&q=80')">
        <h3>Explore The World  1</h3>
      </div>
      <div class="panel" style="background-image: url('https://images.unsplash.com/photo-1572276596237-5db2c3e16c5d?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1350&q=80')">
        <h3>Wild Forest  2</h3>
      </div>
      <div class="panel" style="background-image: url('https://images.unsplash.com/photo-1507525428034-b723cf961d3e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1353&q=80')">
        <h3>Sunny Beach  3</h3>
      </div>
      <div class="panel" style="background-image: url('https://images.unsplash.com/photo-1551009175-8a68da93d5f9?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1351&q=80')">
        <h3>City on Winter 4</h3>
      </div>
      <div class="panel" style="background-image: url('https://images.unsplash.com/photo-1549880338-65ddcdfd017b?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1350&q=80')">
        <h3>Mountains - Clouds  5</h3>
      </div>
    </div>
    <script src="./common.js"></script>
</body>
</html>