├── .gitignore ├── DOC.md ├── package.json ├── 05.字体排印 ├── 23.调整 tab 的宽度.md ├── 20.连字符断行.md ├── 22.文本行的斑马条纹.md ├── 21.插入换行.md ├── 23.调整 tab 的宽度.html └── 22.文本行的斑马条纹.html ├── gulp_codedog.js ├── 06.用户体验 ├── 29.选用合适的鼠标光标.md ├── 31.自定义复选框.md ├── 30.扩大可点击区域.md ├── 32.通过阴影来弱化背景.md ├── 29.选用合适的鼠标光标.html ├── 31.自定义复选框.html └── 30.扩大可点击区域.html ├── 07.结构与布局 ├── 36.自适应内部元素.md ├── 41.紧贴底部的页脚.md ├── 39.满幅的背景,定宽的内容.md ├── 38.根据兄弟元素的数量来设置样式.md ├── 40.垂直居中.md ├── 36.自适应内部元素.html ├── 41.紧贴底部的页脚.html ├── 39.满幅的背景,定宽的内容.html └── 38.根据兄弟元素的数量来设置样式.html ├── gulpfile.js ├── 03.形状 ├── 13.梯形标签页.md ├── 10.平行四边形.md ├── 11.菱形图片.md ├── 09.自适应的椭圆.md ├── 13.梯形标签页.html ├── 09.自适应的椭圆.html ├── 10.平行四边形.html └── 11.菱形图片.html ├── 02.背景与边框 ├── 07.伪随机背景.md ├── 04.边框内圆角.md ├── 02.多重边框.md ├── 01.半透明边框.md ├── 03.灵活的背景定位.md ├── 05.条纹背景.md ├── 08.连续的图像边框.md ├── 06.复杂的背景图案.md ├── 07.伪随机背景.html ├── 04.边框内圆角.html ├── 02.多重边框.html ├── 03.灵活的背景定位.html └── 01.半透明边框.html ├── generate_md.js ├── generate_html.js ├── 04.视觉效果 ├── 15.单侧投影.md └── 15.单侧投影.html └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | index.html -------------------------------------------------------------------------------- /DOC.md: -------------------------------------------------------------------------------- 1 | # DOC 2 | 3 | ```bash 4 | $ npm install 5 | 6 | # 开发 7 | $ npm run dev 8 | 9 | # 部署 10 | $ npm run build 11 | ``` 12 | 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "browser-sync": "^2.18.13", 4 | "codedog": "^2.0.4", 5 | "gulp": "^3.9.1", 6 | "gulp-changed": "^3.2.0", 7 | "marked": "^0.5.2", 8 | "shelljs": "^0.8.3", 9 | "through2": "^3.0.0" 10 | }, 11 | "scripts": { 12 | "dev": "gulp", 13 | "build": "gulp compileAll" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /05.字体排印/23.调整 tab 的宽度.md: -------------------------------------------------------------------------------- 1 | ## 难题 2 | 3 | 我们通常使用 `
` 和 `` 元素来显示代码,它们具有浏览器所赋予的默认样式。
 4 | 
 5 | ```css
 6 | pre, code {
 7 |   font-family: monospace;
 8 | }
 9 | 
10 | pre {
11 |   display: block;
12 |   margin: 1em 0;
13 |   white-space: pre;
14 | }
15 | ```
16 | 
17 | 虽然一般建议用空格来做缩进,但是如果我们用 tab 缩进的话,**浏览器会把其宽度显示为 8 个字符**,非常难看。
18 | 
19 | 
20 | ## 解决方案
21 | 
22 | 我们可以用 CSS3 的新属性 `tab-size`,这个属性接受一个 **数字**(表示字符数)或者一个 **长度值**(这个不实用)。我们通常将其设置为 4 或者 2。
23 | 
24 | ```css
25 | pre {
26 |   tab-size: 2;
27 | }
28 | ```


--------------------------------------------------------------------------------
/gulp_codedog.js:
--------------------------------------------------------------------------------
 1 | const through = require('through2')
 2 | const codedog = require('codedog')
 3 | const path = require('path')
 4 | 
 5 | module.exports = function() {
 6 |   var stream = through.obj(function(file, encoding, callback) {
 7 |     // 修改内容
 8 |     file.contents = new Buffer(codedog(file.contents.toString()))
 9 | 
10 |     // 修改后缀
11 |     let filePath = path.parse(file.path)
12 |     filePath.base = filePath.base.replace(/md/, 'html')
13 |     file.path = path.format(filePath)
14 | 
15 |     // 确保文件会传给下一个插件
16 |     this.push(file)
17 | 
18 |     // 告诉 stream 引擎,已经处理完成
19 |     callback()
20 |   })
21 |   
22 |   return stream
23 | }


--------------------------------------------------------------------------------
/05.字体排印/20.连字符断行.md:
--------------------------------------------------------------------------------
 1 | # 20. 连字符断行
 2 | 
 3 | ```html
 4 | 
 5 | 
 6 | 
 7 |   
17 | 
18 | 
19 | 
20 | The only way to get rid of a temptation is to yield to it 21 |
22 | 23 | 24 | ``` 25 | 26 | 貌似目前(2018.07.17)只有 safari 支持?可是 [caniuse](https://caniuse.com/#feat=css-hyphens) 显示 chrome 和 ff 也支持,但是试了下好像不支持,包括书中的 demo -------------------------------------------------------------------------------- /06.用户体验/29.选用合适的鼠标光标.md: -------------------------------------------------------------------------------- 1 | ## CSS 2 | 3 | 我们可以用 cursor 属性来规定鼠标光标样式,下面列出一些常用的取值: 4 | 5 | - crosshair(十字) 6 | - help(问号) 7 | - move(拖拽) 8 | - pointer(手) 9 | - text(文本框中的鼠标手势) 10 | 11 | ## CSS3 12 | 13 | ### 提示禁用状态 14 | 15 | ```html 16 | 17 | 22 | click me 23 | 24 | ``` 25 | 26 | 值得注意的是,**这个时候这个 a 标签还是可以点击的**,只是视觉上改变了而已。如果要做到不可点击,我们可以使用另外一个样式:`pointer-events: none`,但是这个时候 cursor 样式又不起效了。 27 | 28 | ### 隐藏鼠标光标 29 | 30 | ```html 31 | 32 | 39 | 这是只读的,不要尝试操作我。 40 | 41 | ``` 42 | 43 | 为什么有这个需求?因为有些屏幕并不允许人操作,是只读的。 -------------------------------------------------------------------------------- /05.字体排印/22.文本行的斑马条纹.md: -------------------------------------------------------------------------------- 1 | 我们可以用 `:nth-child()` 或者 `:nth-of-type()` 等伪类来实现斑马条纹,但是如果 **仅对于文本行**,需要另外加上 div,比较麻烦。 2 | 3 | 我们换个思路思考问题,为什么不对整个元素设置统一的背景图像?(参考 05.条纹背景) 4 | 5 | ```html 6 | 7 | 23 |
24 | while (true) {
25 |   var d = new Date();
26 |   if (d.getDate() == 1 && 
27 |     d.getMonth() == 3) {
28 |       alert('hello world');
29 |     }
30 | }
31 | 
32 | 33 | ``` 34 | 35 | - 修改 line-height 同时需要修改 background-size 36 | - background-origin 使得 background-image "平铺" 在 content-box 中 -------------------------------------------------------------------------------- /07.结构与布局/36.自适应内部元素.md: -------------------------------------------------------------------------------- 1 | ## 难题 2 | 3 | ```html 4 | 5 | 10 |
11 | 12 |
this is just a test, believe it or not
13 |
14 | 15 | ``` 16 | 17 | 我们希望文字的最大宽度能和图片一样宽,而且 `div.wrapper` 能水平居中。 18 | 19 | **如何让 `div.wrapper` 的宽度由它内部的图片来决定,而不是由它的父元素来决定呢?** 20 | 21 | 当开发者走投无路时,就只能对 `div.wrapper` 应用一个固定的 width 或 max-width,然后对 `div.wrapper > img` 应用 `max-width: 100%`。可是这个方法无法充分利用有效空间;对于过小的图片来说,布局效果也很突兀。此外,响应式也无从谈起。 22 | 23 | 24 | ## 解决方案 25 | 26 | ```html 27 | 28 | 35 |
36 | 37 |
this is just a test, believe it or not
38 |
39 | 40 | ``` 41 | 42 | CSS3 为 width 和 height 属性定义了一些新的关键字,其中最有用的应该就是 min-content 了。**这个关键字将解析为这个容器内部最大的不可断行元素的宽度(即最宽的单词、图片或具有固定宽度的盒元素)**。 43 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | const gulp = require('gulp') 2 | const codedog = require('./gulp_codedog') 3 | const changed = require('gulp-changed') 4 | const browserSync = require('browser-sync').create() 5 | const shelljs = require('shelljs') 6 | 7 | // 全部文件重新编译 8 | gulp.task("compileAll", () => { 9 | shelljs.exec('node generate_md.js', () => { 10 | return gulp.src(["./**/*.md", "!README.md", "!DOC.md"]) 11 | .pipe(codedog()) 12 | .pipe(gulp.dest("./")) 13 | }) 14 | }) 15 | 16 | // 增量编译 17 | gulp.task("compile", () => { 18 | return gulp.src(["./**/*.md", "!README.md", "!DOC.md"]) 19 | .pipe(changed("./", { // dest 参数需要和 gulp.dest 中的参数保持一致 20 | extension: '.html' // 如果源文件和生成文件的后缀不同,这一行不能忘 21 | })) 22 | .pipe(codedog()) 23 | .pipe(gulp.dest("./")) 24 | .pipe(browserSync.stream()) // reload 25 | }) 26 | 27 | gulp.task('watch', () => { 28 | gulp.watch(['./!(node_modules)/*.md'], ['compile']) 29 | }) 30 | 31 | gulp.task('server', () => { 32 | shelljs.exec('node generate_html.js', () => { 33 | browserSync.init({ 34 | server: { 35 | baseDir: './' 36 | } 37 | }) 38 | }) 39 | }) 40 | 41 | gulp.task('default', ['server', 'watch']) 42 | -------------------------------------------------------------------------------- /03.形状/13.梯形标签页.md: -------------------------------------------------------------------------------- 1 | ```html 2 | 3 | 27 |
ABOUT
28 | 29 | ``` 30 | 31 | 这个方法确实可以生成一个基本的梯形,但还有一个问题没有解决。**当我们没有设置 transform-origin 属性时,应用变形效果会让这个元素以它自身的中心线为轴进行空间上的旋转。**因此,它的宽度会增加,它所占据的位置会稍稍下移,它在高度上会有少许缩减。 32 | 33 | 为了让它的尺寸更好掌握,我们可以为它指定 transform-origin: bottom; **当它在 3D 空间中旋转时,可以把它的底边固定住**。高度上的缩水,可以通过 scaleY() 来弥补。 34 | 35 | ```html 36 | 37 | 62 |
ABOUT
63 | 64 | ``` 65 | 66 | -------------------------------------------------------------------------------- /07.结构与布局/41.紧贴底部的页脚.md: -------------------------------------------------------------------------------- 1 | ```html 2 | 3 | 32 |
this is header
33 |
this is main
34 |
this is footer
35 | 36 | ``` 37 | 38 | 我们所期望的是,页头和页脚的高度由其内部因素来决定,而内容区块的高度应该可以自动伸展并占满所有的可用空间。我们只要给 `
` 这个容器的 flex 属性指定一个大于 0 的值(比如 1),就可以实现这个效果了。 39 | 40 | 这个 flex 属性实际上是 flex-grow、flex-shrink 和 flex-basis 的简写语法。任何元素只要设置了一个大于 0 的 flex 值,就将获得可伸缩的特性。flex 的值负责控制多个可伸缩元素之间的尺寸分配比例。 41 | 42 | 比如我们让 footer 也自动分配高度,为 main 高度的一半。 43 | 44 | ```html 45 | 46 | 75 |
this is header
76 |
this is main
77 |
this is footer
78 | 79 | ``` 80 | -------------------------------------------------------------------------------- /02.背景与边框/07.伪随机背景.md: -------------------------------------------------------------------------------- 1 | # 伪随机背景 2 | 3 | ```html 4 | 5 | 13 | 14 | ``` 15 | 16 | 可以看到,重复规律非常明显 17 | 18 | 为了更真实地模拟条纹的随机性,我们接下来可能会想到,**把这组条纹从一个平面拆散为多个图层**:一种颜色作为底色,另三种颜色作为条纹,然后再让条纹以不同的间隔进行重复平铺。这一点不难做到,我们在色标中定好条纹的宽度,再用 background-size 来控制条纹的间距 19 | 20 | ```html 21 | 22 | 35 | 36 | ``` 37 | 38 | 因为最顶层贴片的重复规律最容易被察觉(它没有被任何东西遮挡),**所以我们应该把平铺间距最大的贴片安排在最顶层** 39 | 40 | 仔细观察,仍然可以看出图像每隔 240px 就会重复一次。其实 240 正是 80 60 40 的最小公倍数(LCM) 41 | 42 | 所以,要让这种随机性更加真实,**我们只要让最小公倍数最大化,让这些数字最好是 "相对质数" 即可** 43 | 44 | ```html 45 | 46 | 59 | 60 | ``` -------------------------------------------------------------------------------- /07.结构与布局/39.满幅的背景,定宽的内容.md: -------------------------------------------------------------------------------- 1 | 满幅的背景,定宽的内容,这个需求很常见。简单说其实就类似块级元素的水平居中。 2 | 3 | 常见的做法: 4 | 5 | ```html 6 | 7 | 20 |
21 |
22 |
23 | 24 | ``` 25 | 26 | margin: auto 发挥了什么作用?**这条声明所产生的左右外边距实际上等于视口宽度的一半减去内容宽度的一半**。我们可以把这个外边距的值表达为 50% - 150px。 27 | 28 | ```html 29 | 30 | 43 |
44 |
45 |
46 | 47 | ``` 48 | 49 | 我们可以进一步把这个长度值应用到父元素的 padding 上: 50 | 51 | ```html 52 | 53 | 66 |
67 |
68 |
69 | 70 | ``` 71 | 72 | 我们还可以把 max-width 一并去掉: 73 | 74 | 75 | ```html 76 | 77 | 89 |
90 |
91 |
92 | 93 | ``` 94 | -------------------------------------------------------------------------------- /02.背景与边框/04.边框内圆角.md: -------------------------------------------------------------------------------- 1 | # 边框内圆角 2 | 3 | ## 难题 4 | 5 | 有时候我们需要一个容器,只在内侧有圆角,而边框或描边的四个角在外部仍然保持直角的形状 6 | 7 | ## 两个元素嵌套 8 | 9 | 用两个元素嵌套很容易实现这个效果: 10 | 11 | ```html 12 | 13 | 26 |
27 |
28 |
29 | 30 | ``` 31 | 32 | ## 更优雅的方案 33 | 34 | ```html 35 | 36 | 49 |
50 | 51 | ``` 52 | 53 | 我们基本受益于两个事实,描边(outline)并不会跟着元素的圆角走(因而显示出直角),但是 box-shadow 却是会的。因此,如果我们把这两者叠加到一起,box-shadow 会刚好填补描边和容器圆角之间的空隙。 54 | 55 | 事实上,指定一个等于描边宽度的扩张值可能会得到异常渲染(目前在 chrome 71 中也是一样,以上 demo 仔细看),因此推荐一个更小的值,那么,这个值是? 56 | 57 | 事实上,我们需要满足 \\((\sqrt2-1)\*r\\)(r 表示 border-radius)< 扩张半径(box-shadow) < 描边宽度(outline)(但是,扩张半径需要比描边宽度至少小多少呢?我测试了下,可能并没有准确的答案),这意味着,**如果描边的宽度比 \\((\sqrt2-1)\*r\\) 小,那我们是不可能用这个方法达成效果**。\\((\sqrt2-1)\\) 约等于 0.414,**如果可以的话,我们可以将扩张半径取值为 border-radius 的一半**。 58 | 59 | ```html 60 | 61 | 74 |
75 | 76 | ``` 77 | 78 | 我们再次强调一遍,该方案的实现完全依赖于 **描边不跟着圆角走这个事实**,但是未来的 CSS 规范明确地建议描边跟着圆角走,我们拭目以待 -------------------------------------------------------------------------------- /03.形状/10.平行四边形.md: -------------------------------------------------------------------------------- 1 | # 平行四边形 2 | 3 | > skew 方法以及伪元素的应用 4 | 5 | ## 难题 6 | 7 | 对元素应用 `transform: skewX(45deg)` 后,虽然得到了一个平行四边形,**但是会导致它的内容也倾向变形**。如何使得只让容器的形状倾斜,而保持内容不变? 8 | 9 | ## 嵌套元素方案 10 | 11 | 我们可以对内容再应用一次反向的 skew() 变形,从而抵消容器的变形效果。但是我们不得不使用一层额外的 HTML 元素来包裹内容,比如用一个 div: 12 | 13 | ```html 14 | 15 | 37 | 38 |
click me
39 |
40 | 41 | ``` 42 | 43 | ## 伪元素方案 44 | 45 | 另一种思路是 **把所有样式**(背景、边框等)**应用到伪元素上**,然后再 **对伪元素进行变形**。因为我们的内容并不是包含在伪元素里的,所以内容并不会受到变形的影响。 46 | 47 | ```html 48 | 49 | 77 | 78 | click me 79 | 80 | 81 | ``` 82 | 83 | 这个技巧不仅对 skew() 变形来说很有用,**还适用于其他任何变形样式**,当我们想 **变形一个元素而不想变形它的内容时** 就可以用到它。 84 | 85 | 这个技巧的关键在于,我们利用伪元素以及定位属性产生一个方块,然后对伪元素设置样式,并将其放置在其宿主元素的下层。 -------------------------------------------------------------------------------- /03.形状/11.菱形图片.md: -------------------------------------------------------------------------------- 1 | ## 基于变形的方案 2 | 3 | 先把图片用一个 div 包裹起来,然后对其应用相反的 rotate() 变形样式,并对图片设置 scale 值为根号 2(尝试着修改代码,去掉 scale(1.42)) 4 | 5 | ```html 6 | 7 | 23 |
24 | 25 |
26 | 27 | ``` 28 | 29 | 但是我们可以发现,**图片被放大后,清晰度下降了**。所以可以给图片指定小于实际长度的长度,使得其放大的情况下清晰度有所改善。 30 | 31 | 注意该方案只适用于正方形图片,不适用于长方形。 32 | 33 | ## 裁剪路径方案 34 | 35 | 我们可以使用 polygon()(多边形)函数来指定一个菱形。实际上,它允许我们用一系列(以逗号分隔的)坐标点来指定任意的多边形。我们甚至可以使用百分比值。 36 | 37 | ```html 38 | 39 | 48 | 49 | 50 | 51 | ``` 52 | 53 | clip-path 甚至可以参与动画,只要我们的动画是在同一种形状函数(比如这里是 polygon())之间进行的,而且点的数量是相同的。因此,如果我们希望图片在鼠标悬停时平滑地扩展为完整的面积,只需要这么做: 54 | 55 | ```html 56 | 57 | 71 | 72 | 73 | ``` 74 | 75 | clip-path 方案可以适用于任何矩形,但是浏览器支持有限 。 76 | 77 | 推荐一个 clip-path marker -------------------------------------------------------------------------------- /06.用户体验/31.自定义复选框.md: -------------------------------------------------------------------------------- 1 | ## 自定义复选框 2 | 3 | 当 `` 元素与复选框关联之后,可以起到触发开关的作用。 4 | 5 | ```html 6 | 7 | 41 | 42 | 43 | 44 | ``` 45 | 46 | ## 开关式按钮 47 | 48 | ```html 49 | 50 | 75 | 76 | 77 | 78 | ``` 79 | -------------------------------------------------------------------------------- /03.形状/09.自适应的椭圆.md: -------------------------------------------------------------------------------- 1 | # 自适应的椭圆 2 | 3 | > CSS 只能生成椭圆、半椭圆以及四分之一椭圆,核心实现就是 border-radius 的应用 4 | 5 | ## 椭圆 6 | 7 | 给元素设置相同宽高以及一半长度的 border-radius,可以得到一个圆形。 8 | 9 | 我们期望能达到这个效果:宽高相等,显示为一个圆;宽高不等,显示一个椭圆。 10 | 11 | 说到 border-radius,有一个鲜为人知的真相:他可以 **单独指定水平和垂直半径**,只要用一个斜杠 `/` 分割这两个值即可。 12 | 13 | ```html 14 | 22 |
23 | ``` 24 | 25 | 我们可以把代码进一步简化,此时去掉 `/` 也是没问题的: 26 | 27 | ```html 28 | 36 |
37 | ``` 38 | 39 | ## 半椭圆 40 | 41 | border-radius 对应的各个展开式属性:(左上角开始,顺时针方向) 42 | 43 | - border-top-left-radius 44 | - border-top-right-radius 45 | - border-bottom-right-radius 46 | - border-bottom-left-radius 47 | 48 | 真正简洁的方法还是使用 border-radius 这个简写属性,我们可以向它一次性提供用空格分开的多个值。当 border-radius 的值为 10px / 5px 20px 时,其效果相当于 10px 10px 10px 10px / 5px 20px 5px 20px。(`/` 前是水平半径,后是垂直半径) 49 | 50 | ```html 51 | 59 |
60 | ``` 61 | 62 | 我们如何分析得到以上的代码呢: 63 | 64 | - 这个形状是垂直对称的,意味着左上角和右上角的半径值应该是相同的;与此类似,左下角和右下角的半径值也应该相同 65 | - 顶部边缘并没有平直部分,这意味着左上角和右上角的水平半径之和应该等于整个形状宽度(即各 50%) 66 | - 再看垂直方向,似乎顶部的两个圆角占据了整个元素的高度,而且底部完全没有任何圆角。因此,在垂直方向上 border-radius 的合理值似乎就是 100% 100% 0 0 67 | - 因为底部两个角的垂直圆角是 0,所以它们的水平圆角是多少完全不重要 68 | 69 | 接下来举一反三,用 CSS 代码来生成一个沿纵轴劈开的半椭圆: 70 | 71 | ```html 72 | 80 |
81 | ``` 82 | 83 | ## 四分之一椭圆 84 | 85 | 要创建一个四分之一椭圆,**其中一个角的水平和垂直半径值都需要是 100%,而其他三个角都不能设为圆角**。 86 | 87 | ```html 88 | 96 |
97 | ``` -------------------------------------------------------------------------------- /06.用户体验/30.扩大可点击区域.md: -------------------------------------------------------------------------------- 1 | 一个简单的 demo: 2 | 3 | ```html 4 | 5 | 18 |
19 | 20 | ``` 21 | 22 | 扩张热区最简单的方法是为它设置一圈透明框,**因为鼠标对元素边框的交互也会触发鼠标事件**。 23 | 24 | ```html 25 | 26 | 42 |
43 | 44 | ``` 45 | 46 | 但是,按钮同时也变大了!**原因在于背景在默认情况下会蔓延到边框的下层。** 47 | 48 | **background-clip 属性可以把背景限制在原本的区域之内。** 49 | 50 | ```html 51 | 52 | 69 |
70 | 71 | ``` 72 | 73 | 但是 hover 的时候 background-clip 并不起效(我怀疑是个 bug @2017.08.28),对于普通的按钮还好,如果是 demo 里这样的需求,非常不友好;而且这个时候如果需要添加真正的边框,就只能用 box-shadow 去模拟了。而且,只能用内嵌投影,外部投影会作用到透明的边框的外面。 74 | 75 | 我们放弃边框,然后改用另外一个特性来实现:**伪元素同样可以代表其宿主元素来响应鼠标交互**。神奇的伪元素! 76 | 77 | ```html 78 | 79 | 102 |
103 | 104 | ``` 105 | 106 | 这个方法的优点是,**我们基本上可以把热区设置为任何想要的尺寸、位置或者形状,甚至可以脱离元素原有的位置!** -------------------------------------------------------------------------------- /generate_md.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | 4 | function readRootDir() { 5 | return new Promise((resolve) => { 6 | fs.readdir("./", (err, files) => { 7 | resolve(files); 8 | }); 9 | }); 10 | } 11 | 12 | function getPromiseArr(files) { 13 | const promiseArr = []; 14 | 15 | // 遍历章 16 | files.forEach((file) => { 17 | // 排除 node_modules 和 .git 两个文件夹的干扰 18 | if (file === "node_modules" || file === ".git") return; 19 | 20 | let path1 = path.join(__dirname, file); 21 | let stats = fs.statSync(path1); 22 | 23 | // 排除类似 generate.js package.json 等文件的干扰 24 | if (!stats.isDirectory()) return; 25 | 26 | promiseArr.push( 27 | new Promise((resolve) => { 28 | const chapter = { 29 | chapterName: file, // 章节名 30 | sections: [], // 子章节名 31 | }; 32 | 33 | fs.readdir(path1, (err, files) => { 34 | // 遍历节 35 | files.forEach((file) => { 36 | if (!file.endsWith(".html")) return; 37 | chapter.sections.push(file); 38 | }); 39 | 40 | resolve(chapter); 41 | }); 42 | }) 43 | ); 44 | }); 45 | 46 | return promiseArr; 47 | } 48 | 49 | function generateMarkdown(res) { 50 | res.sort((a, b) => a.chapterName > b.chapterName); 51 | 52 | let markdownStr = ""; 53 | let urlPrefix = `//lessfish.github.io/css-secrets/`; 54 | 55 | markdownStr += `# CSS SECRETS\n\n`; 56 | 57 | res.forEach((chapter) => { 58 | let { chapterName, sections } = chapter; 59 | 60 | markdownStr += `## ${chapterName}\n\n`; 61 | sections.sort(); 62 | 63 | sections.forEach((sectionName) => { 64 | let url = urlPrefix + chapterName + "/" + sectionName; 65 | url = encodeURI(url); 66 | markdownStr += `- [${sectionName.replace(".html", "")}](${url})\n`; 67 | }); 68 | 69 | markdownStr += "\n"; 70 | }); 71 | 72 | fs.writeFile("README.md", markdownStr, () => { 73 | console.log("README.md saved!"); 74 | }); 75 | } 76 | 77 | (async function () { 78 | const files = await readRootDir(); 79 | const promiseArr = getPromiseArr(files); 80 | 81 | Promise.all(promiseArr) 82 | .then((res) => { 83 | generateMarkdown(res); 84 | }) 85 | .catch((err) => { 86 | console.log(err); 87 | }); 88 | })(); 89 | -------------------------------------------------------------------------------- /07.结构与布局/38.根据兄弟元素的数量来设置样式.md: -------------------------------------------------------------------------------- 1 | ## 难题 2 | 3 | 对于 CSS 选择符来说,基于兄弟元素的总数来匹配元素并不简单。设想一个列表,假设 **仅当列表项的总数为 4 时** 才对这些列表项设置样式。我们可以用 `li:nth-child(4)` 来选中列表的第四个列表项,但这并不是我们想要的;我们需要 **在列表项的总数为 4 时** 选中 **每一个** 列表项。 4 | 5 | ## 解决方案 6 | 7 | ```html 8 | 9 | 17 |
    18 |
  • apple
  • 19 |
  • banana
  • 20 |
  • peach
  • 21 |
  • orange
  • 22 |
23 |
    24 |
  • apple
  • 25 |
  • banana
  • 26 |
  • peach
  • 27 |
28 | 29 | ``` 30 | 31 | 这个方法需要的代码还是冗长繁琐的,我们可以用预处理器优化。 32 | 33 | ## 根据兄弟元素的数量范围来匹配元素 34 | 35 | 列表项的总数是 4 或者更多时选中所有列表项: 36 | 37 | ```html 38 | 39 | 46 |
    47 |
  • apple
  • 48 |
  • banana
  • 49 |
  • peach
  • 50 |
  • orange
  • 51 |
52 |
    53 |
  • apple
  • 54 |
  • banana
  • 55 |
  • peach
  • 56 |
57 | 58 | ``` 59 | 60 | 列表项的总数是 4 或者更少时,选中所有列表项: 61 | 62 | ```html 63 | 64 | 70 |
    71 |
  • apple
  • 72 |
  • banana
  • 73 |
  • peach
  • 74 |
  • orange
  • 75 |
  • watermelon
  • 76 |
77 |
    78 |
  • apple
  • 79 |
  • banana
  • 80 |
  • peach
  • 81 |
82 | 83 | ``` 84 | 85 | 我们也可以把两种技巧组合起来使用,假设我们希望在列表包含 2 ~ 6 个列表项时命中所有的列表项。 86 | 87 | ```html 88 | 89 | 95 |
    96 |
  • apple
  • 97 |
  • banana
  • 98 |
  • peach
  • 99 |
  • orange
  • 100 |
  • watermelon
  • 101 |
102 |
    103 |
  • apple
  • 104 |
  • banana
  • 105 |
  • peach
  • 106 |
107 |
    108 |
  • apple
  • 109 |
110 | 111 | ``` -------------------------------------------------------------------------------- /generate_html.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const path = require('path') 3 | const marked = require('marked') 4 | 5 | function readRootDir() { 6 | return new Promise(resolve => { 7 | fs.readdir('./', (err, files) => { 8 | resolve(files) 9 | }) 10 | }) 11 | } 12 | 13 | function getPromiseArr(files) { 14 | const promiseArr = [] 15 | 16 | // 遍历章 17 | files.forEach(file => { 18 | // 排除 node_modules 和 .git 两个文件夹的干扰 19 | if (file === 'node_modules' || file === '.git') return 20 | 21 | let path1 = path.join(__dirname, file) 22 | let stats = fs.statSync(path1) 23 | 24 | // 排除类似 generate.js package.json 等文件的干扰 25 | if (!stats.isDirectory()) return 26 | 27 | promiseArr.push(new Promise(resolve => { 28 | const chapter = { 29 | chapterName: file, // 章节名 30 | sections: [] // 子章节名 31 | } 32 | 33 | fs.readdir(path1, (err, files) => { 34 | // 遍历节 35 | files.forEach(file => { 36 | if (!file.endsWith('.html')) return 37 | chapter.sections.push(file) 38 | }) 39 | 40 | resolve(chapter) 41 | }) 42 | })) 43 | }) 44 | 45 | return promiseArr 46 | } 47 | 48 | function generateMarkdown(res) { 49 | res.sort((a, b) => a.chapterName > b.chapterName) 50 | 51 | let markdownStr = '' 52 | let urlPrefix = `/` 53 | 54 | markdownStr += `# CSS SECRETS\n\n` 55 | 56 | res.forEach(chapter => { 57 | let {chapterName, sections} = chapter 58 | 59 | markdownStr += `## ${chapterName}\n\n` 60 | sections.sort() 61 | 62 | sections.forEach(sectionName => { 63 | let url = urlPrefix + chapterName + '/' + sectionName 64 | url = encodeURI(url) 65 | markdownStr += `- [${sectionName.replace('.html', '')}](${url})\n` 66 | }) 67 | 68 | markdownStr += '\n' 69 | }) 70 | 71 | let htmlStr = marked(markdownStr) 72 | 73 | // 新标签打开 74 | htmlStr += ` 75 | 76 | ` 77 | 78 | fs.writeFile('index.html', htmlStr, () => { 79 | console.log('index.html saved!') 80 | }) 81 | } 82 | 83 | (async function() { 84 | const files = await readRootDir() 85 | const promiseArr = getPromiseArr(files) 86 | 87 | Promise.all(promiseArr).then(res => { 88 | generateMarkdown(res) 89 | }).catch((err) => {console.log(err)}) 90 | })() -------------------------------------------------------------------------------- /02.背景与边框/02.多重边框.md: -------------------------------------------------------------------------------- 1 | # 多重边框 2 | 3 | ## 难题 4 | 5 | 众所周中,border 可以设置边框,但是有时我们需要多重边框呢? 6 | 7 | ## ugly 方案 8 | 9 | 一个很容易想到的方案是多层元素嵌套,这个方案非常不优雅。 10 | 11 | ## box-shadow 方案 12 | 13 | box-shadow 接受第四个参数(扩张半径),通过指定正值或者负值,可以让投影面积增大或者减小。一个正值的扩张半径加上两个为零的偏移量以及为零的模糊值,得到的 “投影” 其实就像一道实线边框。 14 | 15 | 同时,box-shadow 支持逗号分割语法,我们可以创建任意数量的投影。需要注意的是,box-shadow 是层层叠加的,**第一层投影位于最顶层,依次类推**。 16 | 17 | ```html 18 | 19 | 34 |
35 | 36 | ``` 37 | 38 | 需要注意的点: 39 | 40 | - 投影的行为和边框不完全一致。因为它不影响布局,而且也不会受到 box-sizing 属性的影响。不过,你还是可以通过内边距或者外边距(这取决于投影是内嵌还是外扩的)来额外模拟出边框所需要占据的空间(参考上面的 demo 给 body 加了 padding,不然投影就会到视野外了) 41 | - 投影创造的假边框出现在元素的 **外圈**。它们不会影响鼠标事件,如果这一点非常重要,你可以给 box-shadow 属性加上 inset 关键字,来使投影绘制在元素的 **内圈**。注意此时需要额外增加内边距来腾出足够的空隙(**注意这时候颜色顺序也要跟之前相反,数值也要重新计算过**) 42 | 43 | ```html 44 | 45 | 55 |
56 | 57 | ``` 58 | 59 | ## outline 方案 60 | 61 | 在某些情况下,**你可能只需要两层边框**。可以先设置一层常规边框,再加上 outline(描边)属性来产生外层的边框。这种方法的一大优势在于边框样式十分灵活 62 | 63 | ```html 64 | 65 | 79 |
80 | 81 | ``` 82 | 83 | 描边的另外一个好处在于,你可以通过 outline-offset 属性来控制它跟元素边缘之间的间距,这个属性甚至可以 **接受负值** 84 | 85 | 我们可以实现一个简单的缝边效果: 86 | 87 | ```html 88 | 89 | 99 |
100 | 101 | ``` -------------------------------------------------------------------------------- /02.背景与边框/01.半透明边框.md: -------------------------------------------------------------------------------- 1 | # 半透明边框 2 | 3 | ## 难题 4 | 5 | ```html 6 | 7 | 29 |
30 |
31 |
32 | 33 | ``` 34 | 35 | demo 很简单,白色背景的 div 大小 50\*50px,然后给他加了 10px 的半透明边框,我们希望背景图能够透过来,但是事与愿违,我们看到的白色矩形实际上是 70\*70px。这是因为 **半透明边框透出了这个容器自己的纯白色背景**。 36 | 37 | 默认状态下,背景会延伸到边框的区域下层: 38 | 39 | ```html 40 | 41 | 49 |
50 | 51 | ``` 52 | 53 | 这里的理解我之前有个误区,并不是边框设置了带透明度的颜色,背景就会透上来,**而是背景和边框用了一个颜色**,然后边框又设置了透明度,背景才会透上来。比如上面的 demo 我边框换个颜色 `rgba(125, 255, 255, .5)`,就没有什么问题了(所以感觉实际生产中很少碰到这个问题) 54 | 55 | ## 解决方案 56 | 57 | CSS3 起,我们可以通过设置 background-clip 属性来调整上述默认行为所带来的不便。这个属性的初始值是 border-box,意味着 **背景会被元素的 border box(边框的外沿框)裁减掉**。如果不希望背景侵入边框所在的范围,我们要做的就是把它的值设为 padding-box。 58 | 59 | ```html 60 | 61 | 70 |
71 | 72 | ``` 73 | 74 | ```html 75 | 76 | 99 |
100 |
101 |
102 | 103 | ``` -------------------------------------------------------------------------------- /02.背景与边框/03.灵活的背景定位.md: -------------------------------------------------------------------------------- 1 | # 灵活的背景定位 2 | 3 | ## 难题 4 | 5 | 很多时候,我们想针对容器某个角对背景图片做偏移定位,如右下角。在 CSS 2.1 中,**我们只能指定距离左上角的偏移量**,或者干脆完全靠齐到其他三个角。但是,我们有时希望图片和容器的边角之间能留出一定的空隙(类似内边距的效果) 6 | 7 | 对于具有固定尺寸的容器来说,使用 CSS 2.1 来做到这一点是可能的,但很麻烦:可以基于它自身的尺寸以及我们期望它距离右下角的偏移量,**计算出背景图片距离左上角的偏移量**,然后再把计算结果设置给 background-position。当容器元素尺寸不固定时(因为内容往往是可变的),这就不可能做到了。开发者通常只能把 background-position 设置为某个接近 100% 的百分比值 8 | 9 | 所以我们的挑战是,**把背景图片定位到距离底边 10px 且距离右边 20px 的位置** 10 | 11 | ## background-position 的扩展语法方案 12 | 13 | 在 CSS3 中,background-position 属性已经得到扩展,它允许我们指定背景图片 **距离任意角的偏移量**,只要我们 **在偏移量前面指定关键字** 14 | 15 | ```html 16 | 17 | 26 |
27 | 28 | ``` 29 | 30 | ## background-origin 方案 31 | 32 | 在给背景图片设置距离某个角的偏移量时,有一种情况极其常见:**偏移量和容器的内边距一致**。如果采用上面提到的 background-position 的扩展语法方案,代码如下: 33 | 34 | ```html 35 | 36 | 45 |
46 | 47 | ``` 48 | 49 | 代码不够优雅,如需改动需要在三个地方更新值 50 | 51 | 还有一个更简单的方法可以实现这个需求,**让它自动跟着我们设定的内边距走**,不需要另外声明偏移的量 52 | 53 | ```html 54 | 55 | 64 |
65 | 66 | ``` 67 | 68 | 默认情况下,**background-position 是以 padding box 为准的,这样边框才不会遮住背景图片**。因此,top left 默认指的是 padding box 的左上角。background-origin 默认取值 padding-box,还可以取值 content-box,border-box 69 | 70 | ## calc() 方案 71 | 72 | 如果我们仍然 **以左上角偏移的思路** 来考虑,其实就是希望它有一个 100% - 20px 的水平偏移量,以及 100% - 10px 的垂直偏移量。calc() 可以做到这点(其实这里 calc() 为啥这样能实现效果我不是很清楚,为啥 `background-position: calc(100%) calc(100%)` 背景图就能去右下角了?) 73 | 74 | ```html 75 | 76 | 84 |
85 | 86 | ``` -------------------------------------------------------------------------------- /04.视觉效果/15.单侧投影.md: -------------------------------------------------------------------------------- 1 | ## box-shadow 2 | 3 | 很多人使用 box-shadow 的方法是,指定三个长度值和一个颜色值。 4 | 5 | ```html 6 | 7 | 15 |
16 | 17 | ``` 18 | 19 | 1. 以该元素相同的尺寸和位置,画一个背景色为 black 的矩形 20 | 2. 把它向右移 5px,向下移 4px 21 | 3. 使用高斯模糊算法(或类似算法)将它进行 4px 的模糊处理。这在本质上表示在阴影边缘发生阴影色和纯透明色之间的颜色过渡长度近似于模糊半径的两倍(比如这里是 8px) 22 | 4. **模糊后的矩形与原始元素的交集部分会被切除掉** 23 | 24 | 但是这在某种程度上会导致外露的投影太过浓重,看起来不是很美观。 25 | 26 | 27 | ## 单侧投影 28 | 29 | 解决方案来自 box-shadow 鲜为人知的第四个长度参数,称为扩张半径。这个值会根据你指定的值去扩大或(当指定负值)缩小投影的尺寸。 30 | 31 | 从逻辑上来讲,如果我们应用一个负的扩张半径,而它的值刚好等于模糊半径,那么投影的尺寸就会和投影所属元素的尺寸完全一致。除非用偏移量来移动它,**否则我们将看不到任何投影**。 32 | 33 | ```html 34 | 35 | 50 |
51 |
52 |
53 | 54 | ``` 55 | 56 | 因此,我们给投影应用一个正的垂直偏移量即可。 57 | 58 | ```html 59 | 60 | 71 |
72 | 73 | ``` 74 | 75 | ## 邻边投影 76 | 77 | 传统的应用 box-shadow 会使得阴影太过浓厚。 78 | 79 | ```html 80 | 81 | 92 |
93 | 94 | ``` 95 | 96 | 我们可以这样做(将扩张半径设置为模糊半径相反值的一半): 97 | 98 | ```html 99 | 100 | 111 |
112 | 113 | ``` 114 | 115 | ## 双侧阴影 116 | 117 | 把单侧投影中的技巧应用两次。 118 | 119 | ```html 120 | 121 | 133 |
134 | 135 | ``` -------------------------------------------------------------------------------- /05.字体排印/21.插入换行.md: -------------------------------------------------------------------------------- 1 | ## 难题 2 | 3 | ```html 4 | 5 | 11 |
12 |
Name:
13 |
Fish
14 | 15 |
Sex:
16 |
male
17 | 18 |
Location:
19 |
China
20 |
21 | 22 | ``` 23 | 24 | 我们希望的效果是 dd 元素和相应的 dt 元素能在一行。 25 | 26 | 先对 dt 和 dd 应用 `display: inline`,然后在适当的时候添加 `
` 可以做到: 27 | 28 | ```html 29 | 30 | 40 |
41 |
Name:
42 |
Fish
43 |
44 | 45 |
Sex:
46 |
male
47 |
48 | 49 |
Location:
50 |
China
51 |
52 |
53 | 54 | ``` 55 | 56 | 使用 `
` 非常不优雅,实际上,有一个 Unicode 字符是专门代表换行符的:0x000A。在 CSS 中,这个字符可以写作 "\000A",或简化为 "\A"。我们可以用它作为 ::after 伪元素的内容,并将其添加到每个 `
` 元素的尾部。 57 | 58 | ```html 59 | 60 | 74 |
75 |
Name:
76 |
Fish
77 | 78 |
Sex:
79 |
male
80 | 81 |
Location:
82 |
China
83 |
84 | 85 | ``` 86 | 87 | WTF? 没起效!这段 CSS 代码所做的其实只相当于在 HTML 结构中的所有关闭标签 `
` 之前添加换行符,**默认情况下,这些换行符会与相邻的其他空白符进行合并**。空白符合并通常是一件非常好的事情,不过,**有时候我们希望保留源代码中的这些空白符和换行**。 88 | 89 | ```html 90 | 91 | 106 |
107 |
Name:
108 |
Fish
109 | 110 |
Sex:
111 |
male
112 | 113 |
Location:
114 |
China
115 |
116 | 117 | ``` 118 | 119 | 但是这个方法 **不健壮**,我们尝试添加第二个地址: 120 | 121 | ```html 122 | 123 | 138 |
139 |
Name:
140 |
Fish
141 | 142 |
Sex:
143 |
male
144 | 145 |
Location:
146 |
China
147 |
Hangzhou
148 |
149 | 150 | ``` 151 | 152 | 问题出现了,我们可以尝试对 dt 元素设置样式: 153 | 154 | ```html 155 | 156 | 171 |
172 |
Name:
173 |
Fish
174 | 175 |
Sex:
176 |
male
177 | 178 |
Location:
179 |
China
180 |
Hangzhou
181 |
182 | 183 | ``` 184 | -------------------------------------------------------------------------------- /05.字体排印/23.调整 tab 的宽度.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 78 | 79 | 80 |

难题

81 |

我们通常使用 <pre><code> 元素来显示代码,它们具有浏览器所赋予的默认样式。

82 |
pre, code {
 83 |   font-family: monospace;
 84 | }
 85 | 
 86 | pre {
 87 |   display: block;
 88 |   margin: 1em 0;
 89 |   white-space: pre;
 90 | }
 91 | 
92 |

虽然一般建议用空格来做缩进,但是如果我们用 tab 缩进的话,浏览器会把其宽度显示为 8 个字符,非常难看。

93 |

解决方案

94 |

我们可以用 CSS3 的新属性 tab-size,这个属性接受一个 数字(表示字符数)或者一个 长度值(这个不实用)。我们通常将其设置为 4 或者 2。

95 |
pre {
 96 |   tab-size: 2;
 97 | }
 98 | 
99 |
100 | 101 | 120 | 121 | -------------------------------------------------------------------------------- /02.背景与边框/05.条纹背景.md: -------------------------------------------------------------------------------- 1 | # 条纹背景 2 | 3 | ## 难题 4 | 5 | 以前想要在网页中实现条纹图案,可能需要创建一个单独的位图文件,然后每次需要做些调整时,都用图像编辑器来修改它。现在我们可以直接在 CSS 中创建条纹图案 6 | 7 | ## 水平条纹 8 | 9 | 该法能实现水平条纹的核心原因是:如果多个色标具有相同的位置,它们会产生一个无限小的过渡区域 10 | 11 | ```html 12 | 13 | 21 | 22 | ``` 23 | 24 | 以上 demo 中每个条纹的高度是 15px(它有两个渐变色,分别占据 50%,总共 30px) 25 | 26 | 为了避免每次改动数值时需要修改两个数字,我们可以再次从规范那里找到捷径: 27 | 28 | > 如果某个色标的位置值比整个列表中在它之前的色标的位置值都要小,则该色标的位置值会被设置为它前面所有色标位置值的最大值 29 | 30 | ```html 31 | 32 | 38 | 39 | ``` 40 | 41 | ## 垂直条纹 42 | 43 | ```html 44 | 45 | 51 | 52 | ``` 53 | 54 | 与水平条纹实现的主要差别在于:我们需要在开头加上一个额外的参数来指定渐变的方向。在水平条纹的代码中,我们其实也可以加上这个参数,只不过它的默认值 to bottom 本来就跟我们的意图一致,所以可省略。最后,我们还需要把 background-size 的值颠倒一下 55 | 56 | ## 斜向条纹 57 | 58 | ```html 59 | 60 | 66 | 67 | ``` 68 | 69 | 这个方法行不通。原因在于 **单个切片需要包含四条条纹**,而不是两条,只有这样才能做到无缝拼接 70 | 71 | ```html 72 | 73 | 81 | 82 | ``` 83 | 84 | 但是这样的结果条纹很明显比之前的细,根据计算,**我们需要设置单个切片的宽度是之前的 \\(\sqrt2\\) 倍** 85 | 86 | ```html 87 | 88 | 96 | 97 | ``` 98 | 99 | ## 更好的斜向条纹 100 | 101 | 我们将上面的代码修改角度,会发现条纹完全错乱了(try it!) 102 | 103 | 幸运的是,我们还有更好的方法来创建斜向条纹。linear-gradient() 和 radial-gradient() 还各有一个循环式的加强版:repeating-linear-gradient() 和 repeating-radial-gradient()。它们的工作方式和前两者类似,只有一点不同:**色标是无限循环重复的,直到填满整个背景**。 104 | 105 | ```html 106 | 107 | 113 | 114 | ``` 115 | 116 | - 可以随意变换角度(try it!) 117 | - 15px 是指渐变轴上度量到的距离,再也不用去乘以 \\(\sqrt2\\) 了 118 | - 同理可以生成水平和垂直条纹(但是请注意,在这个方法中,无论条纹的角度如何,我们在创建双色条纹时都需要用到四个色标,这意味着,**我们最好用前面的方法来实现水平条纹或者垂直条纹,而用这种方法实现斜向条纹**) 119 | 120 | ## 灵活的同色系条纹 121 | 122 | 大多数情况下,我们想要的条纹图案并不是由差异极大的几种颜色组成的,**这些颜色往往属于同一色系,只是在明度方面有着轻微的差异** 123 | 124 | ```html 125 | 126 | 132 | 133 | ``` 134 | 135 | 条纹是由一个主色调(#58a)和它的浅色变体所组成的。但是,这两种颜色之间的关系在代码中并没有体现出来。如果我们想要改变这个条纹的主色调,甚至需要修改四处! 136 | 137 | 幸运的是,还有一种更好的方法:不再为每种条纹单独指定颜色,**而是把最深的颜色指定为背景色**,同时把半透明白色的条纹叠加在背景色之上来得到浅色条纹 138 | 139 | ```html 140 | 141 | 150 | 151 | ``` 152 | -------------------------------------------------------------------------------- /02.背景与边框/08.连续的图像边框.md: -------------------------------------------------------------------------------- 1 | # 连续的图像边框 2 | 3 | 我们希望实现如下的效果,但是只用一个元素: 4 | 5 | ```html 6 | 7 | 19 |
20 |
21 | I have a nice stone art border, 22 | don't I look pretty? 23 |
24 |
25 | 26 | ``` 27 | 28 | 主要的思路是 **在背景图片之上,再叠加一层纯白的实色背景** 29 | 30 | ```html 31 | 32 | 44 |
45 | I have a nice stone art border, 46 | don't I look pretty? 47 |
48 | 49 | ``` 50 | 51 | 但是边框的图片看起来有一种怪异拼接的感觉,原因在于 background-origin 的默认值是 padding-box,因此,图片的显示尺寸不仅取决于 padding-box 的尺寸,而且被放置在了 padding box 的原点(左上角)。我们看到的实际上就是背景图片以平铺的方式蔓延到 border box 区域的结果。为了修正这个问题,只需把 background-origin 也设置为 border-box 就可以了。 52 | 53 | ```html 54 | 55 | 68 |
69 | I have a nice stone art border, 70 | don't I look pretty? 71 |
72 | 73 | ``` 74 | 75 | 可以简写为: 76 | 77 | ```html 78 | 79 | 89 |
90 | I have a nice stone art border, 91 | don't I look pretty? 92 |
93 | 94 | ``` 95 | 96 | 这个技巧还可以用在 **渐变图案** 上,下面这段代码可以生成一种 **老式信封样式的边框**: 97 | 98 | ```html 99 | 100 | 115 |
116 | My border is reminiscent of vintage envelopes, how cool is that? 117 |
118 | 119 | ``` 120 | 121 | 这个技巧的另一个用武之地是创建好玩的 **蚂蚁行军边框** 122 | 123 | ```html 124 | 125 | 140 |
141 | 142 | ``` 143 | 144 | 145 | -------------------------------------------------------------------------------- /07.结构与布局/40.垂直居中.md: -------------------------------------------------------------------------------- 1 | ## 基于绝对定位的解决方案 2 | 3 | ```html 4 | 5 | 24 |
25 |
26 |
27 | 28 | ``` 29 | 30 | 代码很容易理解:先把这个元素的左上角放在视口(或最近的、具有定位属性的祖先元素)的正中心,然后再利用负外边距把它向左、向上移动(移动距离相当于它自身宽高的一半)。借助强大的 calc() 函数,这段代码还可以省略两行声明。 31 | 32 | ```html 33 | 34 | 51 |
52 |
53 |
54 | 55 | ``` 56 | 57 | 这个方法最大的局限在于它 **要求元素的宽高是固定的**。 58 | 59 | 通常情况下,对那些需要居中的元素来说,其尺寸往往是由其内容来决定的。**如果能找到一个属性的百分比值以元素自身的宽高作为解析基准**,那我们的难题就迎刃而解了!在这个例子中,答案来自 CSS 变形属性。当我们在 translate() 变形函数中使用百分比值时,是以这个元素自身的宽度和高度为基准进行换算和移动的,这样我们就可以彻底解除对固定尺寸的依赖,该方案适用于固定尺寸以及不固定尺寸。 60 | 61 | 固定宽高: 62 | 63 | ```html 64 | 65 | 83 |
84 |
85 |
86 | 87 | ``` 88 | 89 | 不固定尺寸: 90 | 91 | ```html 92 | 93 | 111 |
112 |
113 |
114 |
115 | 116 | ``` 117 | 118 | ## 基于视口单位的解决方案 119 | 120 | 如果你只想把元素 **相对于视口进行居中**,可以试试此方案。 121 | 122 | - vw 是与视口宽度相关的。1vw 表示视口宽度的 1% 123 | - 1vh 表示视口高度的 1% 124 | - 当视口宽度小于高度时,1vmin 等于 1vw,否则等于 1vh 125 | - 当视口宽度小于高度时,1vmax 等于 1vw,否则等于 1vh 126 | 127 | 可以利用 margin 同时完成水平和垂直的居中,但是要时刻记住它的局限性(仅 **相对于视口进行居中**) 128 | 129 | ```html 130 | 131 | 141 |
142 | 143 | ``` 144 | 145 | ## Flexbox 146 | 147 | 利用 flexbox,我们只需要写两行声明:先给这个待居中元素的父元素设置 `display: flex`,再给这个元素自身设置 `margin: auto`。请注意,**当我们使用 flexbox 时,`margin: auto` 不仅在水平方向上将元素居中,垂直方向上也是如此。** 148 | 149 | 150 | ```html 151 | 152 | 166 |
167 |
168 |
169 | 170 | ``` 171 | 172 | flexbox 另一个好处在于,它还可以将匿名元素(即没有被标签包裹的文本节点)垂直居中。 173 | 174 | ```html 175 | 176 | 188 |
189 | nothing's gonna change my love for you 190 |
191 | 192 | ``` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CSS SECRETS 2 | 3 | ## 02.背景与边框 4 | 5 | - [01.半透明边框](//lessfish.github.io/css-secrets/02.%E8%83%8C%E6%99%AF%E4%B8%8E%E8%BE%B9%E6%A1%86/01.%E5%8D%8A%E9%80%8F%E6%98%8E%E8%BE%B9%E6%A1%86.html) 6 | - [02.多重边框](//lessfish.github.io/css-secrets/02.%E8%83%8C%E6%99%AF%E4%B8%8E%E8%BE%B9%E6%A1%86/02.%E5%A4%9A%E9%87%8D%E8%BE%B9%E6%A1%86.html) 7 | - [03.灵活的背景定位](//lessfish.github.io/css-secrets/02.%E8%83%8C%E6%99%AF%E4%B8%8E%E8%BE%B9%E6%A1%86/03.%E7%81%B5%E6%B4%BB%E7%9A%84%E8%83%8C%E6%99%AF%E5%AE%9A%E4%BD%8D.html) 8 | - [04.边框内圆角](//lessfish.github.io/css-secrets/02.%E8%83%8C%E6%99%AF%E4%B8%8E%E8%BE%B9%E6%A1%86/04.%E8%BE%B9%E6%A1%86%E5%86%85%E5%9C%86%E8%A7%92.html) 9 | - [05.条纹背景](//lessfish.github.io/css-secrets/02.%E8%83%8C%E6%99%AF%E4%B8%8E%E8%BE%B9%E6%A1%86/05.%E6%9D%A1%E7%BA%B9%E8%83%8C%E6%99%AF.html) 10 | - [06.复杂的背景图案](//lessfish.github.io/css-secrets/02.%E8%83%8C%E6%99%AF%E4%B8%8E%E8%BE%B9%E6%A1%86/06.%E5%A4%8D%E6%9D%82%E7%9A%84%E8%83%8C%E6%99%AF%E5%9B%BE%E6%A1%88.html) 11 | - [07.伪随机背景](//lessfish.github.io/css-secrets/02.%E8%83%8C%E6%99%AF%E4%B8%8E%E8%BE%B9%E6%A1%86/07.%E4%BC%AA%E9%9A%8F%E6%9C%BA%E8%83%8C%E6%99%AF.html) 12 | - [08.连续的图像边框](//lessfish.github.io/css-secrets/02.%E8%83%8C%E6%99%AF%E4%B8%8E%E8%BE%B9%E6%A1%86/08.%E8%BF%9E%E7%BB%AD%E7%9A%84%E5%9B%BE%E5%83%8F%E8%BE%B9%E6%A1%86.html) 13 | 14 | ## 03.形状 15 | 16 | - [09.自适应的椭圆](//lessfish.github.io/css-secrets/03.%E5%BD%A2%E7%8A%B6/09.%E8%87%AA%E9%80%82%E5%BA%94%E7%9A%84%E6%A4%AD%E5%9C%86.html) 17 | - [10.平行四边形](//lessfish.github.io/css-secrets/03.%E5%BD%A2%E7%8A%B6/10.%E5%B9%B3%E8%A1%8C%E5%9B%9B%E8%BE%B9%E5%BD%A2.html) 18 | - [11.菱形图片](//lessfish.github.io/css-secrets/03.%E5%BD%A2%E7%8A%B6/11.%E8%8F%B1%E5%BD%A2%E5%9B%BE%E7%89%87.html) 19 | - [13.梯形标签页](//lessfish.github.io/css-secrets/03.%E5%BD%A2%E7%8A%B6/13.%E6%A2%AF%E5%BD%A2%E6%A0%87%E7%AD%BE%E9%A1%B5.html) 20 | 21 | ## 04.视觉效果 22 | 23 | - [15.单侧投影](//lessfish.github.io/css-secrets/04.%E8%A7%86%E8%A7%89%E6%95%88%E6%9E%9C/15.%E5%8D%95%E4%BE%A7%E6%8A%95%E5%BD%B1.html) 24 | 25 | ## 05.字体排印 26 | 27 | - [20.连字符断行](//lessfish.github.io/css-secrets/05.%E5%AD%97%E4%BD%93%E6%8E%92%E5%8D%B0/20.%E8%BF%9E%E5%AD%97%E7%AC%A6%E6%96%AD%E8%A1%8C.html) 28 | - [21.插入换行](//lessfish.github.io/css-secrets/05.%E5%AD%97%E4%BD%93%E6%8E%92%E5%8D%B0/21.%E6%8F%92%E5%85%A5%E6%8D%A2%E8%A1%8C.html) 29 | - [22.文本行的斑马条纹](//lessfish.github.io/css-secrets/05.%E5%AD%97%E4%BD%93%E6%8E%92%E5%8D%B0/22.%E6%96%87%E6%9C%AC%E8%A1%8C%E7%9A%84%E6%96%91%E9%A9%AC%E6%9D%A1%E7%BA%B9.html) 30 | - [23.调整 tab 的宽度](//lessfish.github.io/css-secrets/05.%E5%AD%97%E4%BD%93%E6%8E%92%E5%8D%B0/23.%E8%B0%83%E6%95%B4%20tab%20%E7%9A%84%E5%AE%BD%E5%BA%A6.html) 31 | - [24.连字](//lessfish.github.io/css-secrets/05.%E5%AD%97%E4%BD%93%E6%8E%92%E5%8D%B0/24.%E8%BF%9E%E5%AD%97.html) 32 | 33 | ## 06.用户体验 34 | 35 | - [29.选用合适的鼠标光标](//lessfish.github.io/css-secrets/06.%E7%94%A8%E6%88%B7%E4%BD%93%E9%AA%8C/29.%E9%80%89%E7%94%A8%E5%90%88%E9%80%82%E7%9A%84%E9%BC%A0%E6%A0%87%E5%85%89%E6%A0%87.html) 36 | - [30.扩大可点击区域](//lessfish.github.io/css-secrets/06.%E7%94%A8%E6%88%B7%E4%BD%93%E9%AA%8C/30.%E6%89%A9%E5%A4%A7%E5%8F%AF%E7%82%B9%E5%87%BB%E5%8C%BA%E5%9F%9F.html) 37 | - [31.自定义复选框](//lessfish.github.io/css-secrets/06.%E7%94%A8%E6%88%B7%E4%BD%93%E9%AA%8C/31.%E8%87%AA%E5%AE%9A%E4%B9%89%E5%A4%8D%E9%80%89%E6%A1%86.html) 38 | - [32.通过阴影来弱化背景](//lessfish.github.io/css-secrets/06.%E7%94%A8%E6%88%B7%E4%BD%93%E9%AA%8C/32.%E9%80%9A%E8%BF%87%E9%98%B4%E5%BD%B1%E6%9D%A5%E5%BC%B1%E5%8C%96%E8%83%8C%E6%99%AF.html) 39 | 40 | ## 07.结构与布局 41 | 42 | - [36.自适应内部元素](//lessfish.github.io/css-secrets/07.%E7%BB%93%E6%9E%84%E4%B8%8E%E5%B8%83%E5%B1%80/36.%E8%87%AA%E9%80%82%E5%BA%94%E5%86%85%E9%83%A8%E5%85%83%E7%B4%A0.html) 43 | - [38.根据兄弟元素的数量来设置样式](//lessfish.github.io/css-secrets/07.%E7%BB%93%E6%9E%84%E4%B8%8E%E5%B8%83%E5%B1%80/38.%E6%A0%B9%E6%8D%AE%E5%85%84%E5%BC%9F%E5%85%83%E7%B4%A0%E7%9A%84%E6%95%B0%E9%87%8F%E6%9D%A5%E8%AE%BE%E7%BD%AE%E6%A0%B7%E5%BC%8F.html) 44 | - [39.满幅的背景,定宽的内容](//lessfish.github.io/css-secrets/07.%E7%BB%93%E6%9E%84%E4%B8%8E%E5%B8%83%E5%B1%80/39.%E6%BB%A1%E5%B9%85%E7%9A%84%E8%83%8C%E6%99%AF%EF%BC%8C%E5%AE%9A%E5%AE%BD%E7%9A%84%E5%86%85%E5%AE%B9.html) 45 | - [40.垂直居中](//lessfish.github.io/css-secrets/07.%E7%BB%93%E6%9E%84%E4%B8%8E%E5%B8%83%E5%B1%80/40.%E5%9E%82%E7%9B%B4%E5%B1%85%E4%B8%AD.html) 46 | - [41.紧贴底部的页脚](//lessfish.github.io/css-secrets/07.%E7%BB%93%E6%9E%84%E4%B8%8E%E5%B8%83%E5%B1%80/41.%E7%B4%A7%E8%B4%B4%E5%BA%95%E9%83%A8%E7%9A%84%E9%A1%B5%E8%84%9A.html) 47 | 48 | --- 49 | 50 | 一点思考: 51 | 52 | - html 文件放到 GitHub 的 CI 中去生成去跑去生成?本地的 ignore 掉? 53 | -------------------------------------------------------------------------------- /06.用户体验/32.通过阴影来弱化背景.md: -------------------------------------------------------------------------------- 1 | # 32. 通过阴影来弱化背景 2 | 3 | ## 难题 4 | 5 | 很多时候,我们需要通过一层半透明的遮罩层来把后面的一切整体调暗,以便凸显某个特定的 UI 元素,引导用户关注。这个效果最常见的实现方式就是增加一个额外的 HTML 元素用于遮挡背景(`.overlay` 类): 6 | 7 | ```html 8 | 38 | 39 |
页面
40 | 41 |
42 | ``` 43 | 44 | 这里我们直接显示遮罩层了,通常情况下是需要 js 交互触发才显示的 45 | 46 | 我们可以看下 bootstrap 的 [modal](https://getbootstrap.com/docs/4.0/components/modal/) 实现,思路类似,当 modal 出现时,body 元素会加上 `modal-open` 这个类,其实就是添加 `overflow: hidden` 样式,使得背景不可滚动(这不是本文重点,只是顺带提一下) 47 | 48 | 在代码中,`.lightbox` 需要指定一个更高的 z-index,以便绘制在遮罩层的上层(当然可以有其他办法,比如将 `.lightbox` 当作 `.overlay` 的子元素,或者将 `.lightbox` 当作 `.overlay` 的后一个兄弟元素,只是设置了 z-index 比较保险,不需要再考虑那些情况) 49 | 50 | 这个方法需要增加一个额外的 HTML 元素(`.overlay`,这里不考虑 `.lightbox`)这意味着该效果无法由 CSS 单独实现,不过,我们有其他办法可以摆脱这个麻烦 51 | 52 | ## 伪元素方案 53 | 54 | 我们可以利用伪元素来消除额外的 HTML 元素: 55 | 56 | ```html 57 | 58 | 91 | 92 |
页面
93 | 94 | 95 | ``` 96 | 97 | 这样实现有几个问题: 98 | 99 | - `body::before` 可能已经被占用 100 | - 在使用这个效果时,我们需要用 js 给 body 元素添加 `dimmend` 这个类 101 | - 伪元素无法绑定独立的 js 事件处理 102 | 103 | 104 | 后两个问题是无法解决的,针对第一个问题,我们可以把遮罩层交给这个元素自己的 `::before` 伪元素实现: 105 | 106 | ```html 107 | 139 | 140 |
页面
141 | 142 | ``` 143 | 144 | 给伪元素设置 `z-index: -1`,就可以让它出现在元素的背后,但无法对遮罩层的 Z 轴进行细粒度的控制。它可能出现在这个元素之后(我们期望),也可能会出现在这个元素的父元素或祖先元素之后 145 | 146 | 147 | ## box-shadow 方案 148 | 149 | 对于简单的应用场景来说,我们可以利用 `box-shadow`: 150 | 151 | ```html 152 | 177 | 178 | 179 | 180 | ``` 181 | 182 | **该方法有个严重的问题,就是无法阻止用户和页面的其他部分交互** 183 | 184 | ## backdrop 方案 185 | 186 | ```html 187 | 196 | 197 | 198 | 199 | O HAI! 200 | 201 | 202 | ``` 203 | 204 | 注意下浏览器兼容性 205 | -------------------------------------------------------------------------------- /05.字体排印/22.文本行的斑马条纹.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 78 | 79 | 80 |

我们可以用 :nth-child() 或者 :nth-of-type() 等伪类来实现斑马条纹,但是如果 仅对于文本行,需要另外加上 div,比较麻烦。

81 |

我们换个思路思考问题,为什么不对整个元素设置统一的背景图像?(参考 05.条纹背景)

82 |
83 |
84 | 85 |
86 |
87 |
88 |
89 | 90 |
91 |
92 |
93 | 160 |
    161 |
  • 修改 line-height 同时需要修改 background-size
  • 162 |
  • background-origin 使得 background-image "平铺" 在 content-box 中
  • 163 |
164 |
165 | 166 | 185 | 186 | -------------------------------------------------------------------------------- /02.背景与边框/06.复杂的背景图案.md: -------------------------------------------------------------------------------- 1 | # 复杂的背景图案 2 | 3 | ## 网格 4 | 5 | 当我们把多个渐变图案组合起来,让它们透过彼此的透明区域显现时,可以得到各种各样的网格 6 | 7 | ```html 8 | 9 | 19 | 20 | ``` 21 | 22 | 在某些情况下,我们希望 **网格中每个格子的大小可以调整,而网格线条的粗细同时保持固定** 23 | 24 | 以下是一个 **使用长度而不是百分比** 作为色标的例子: 25 | 26 | ```html 27 | 28 | 37 | 38 | ``` 39 | 40 | 在开发 [segmentfault 广告模块](https://segmentfault.com/sponsor) 的时候,我就用到了这个技巧(背景部分,忽略红色部分) 41 | 42 | ![](https://ws3.sinaimg.cn/large/006tNbRwly1fy8tuyiylgj30h80bwt92.jpg) 43 | 44 | ## 波点 45 | 46 | ```html 47 | 48 | 55 | 56 | ``` 57 | 58 | 我们可以生成两层圆点阵列图案,并把它们的背景定位错开,从而导得到真正的波点图案。请注意,为了达到效果,**第二层背景的偏移定位值必须是贴片宽高的一半** 59 | 60 | ```html 61 | 62 | 72 | 73 | ``` 74 | 75 | ## 棋盘 76 | 77 | 这里的窍门在于 **用两个直角三角形来拼合出我们想要的方块** 78 | 79 | 一个直角三角形: 80 | 81 | ```html 82 | 83 | 91 | 92 | ``` 93 | 94 | 我们把这些直角三角形的直角边缩短到原来的一半 95 | 96 | ```html 97 | 98 | 106 | 107 | ``` 108 | 109 | 我们把色标的顺序反转,将两者组合 110 | 111 | ```html 112 | 113 | 122 | 123 | ``` 124 | 125 | 然后把第二层渐变在水平和垂直方向均移动贴片长度的一半,就可以拼成一个完整的方块 126 | 127 | ```html 128 | 129 | 139 | 140 | ``` 141 | 142 | 这本质上只是 **棋盘的一半**,我们只需要把现有的这一组渐变重复一份,从而创建出另一组正方形 143 | 144 | 145 | ```html 146 | 147 | 163 | 164 | ``` 165 | 166 | 代码还可以优化,将以上 background-image 中 1、4 划为一组,2、3 划为一组,把四层渐变简化成两层渐变,我们还可以把深灰色改成半透明的黑色 —— 这样我们只需要修改底色就可以改变整个棋盘的色调,不需要单独调整各层渐变的色标了 167 | 168 | ```html 169 | 170 | 186 | 187 | ``` 188 | 189 | 但是显示貌似有个问题,有莫名的黑色斜线(如下图) 190 | 191 | ![](https://ws4.sinaimg.cn/large/006tNbRwly1fy8tt175rej30oa0eodgb.jpg) 192 | 193 | 其实,svg 是更好的选择 194 | 195 | ```html 196 | 198 | 199 | 200 | 201 | ``` 202 | 203 | 对于现代浏览器,svg 文件可以以 data URI 的形式嵌入到样式表中,甚至不需要用 base64 或 URLencode 来对其编码,这样跟 CSS 渐变一样也能省掉 HTTP 请求(普通的 svg 文件会增加 http 请求) 204 | 205 | ```html 206 | 207 | 218 | 219 | ``` 220 | -------------------------------------------------------------------------------- /06.用户体验/29.选用合适的鼠标光标.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 78 | 79 | 80 |

CSS

81 |

我们可以用 cursor 属性来规定鼠标光标样式,下面列出一些常用的取值:

82 |
    83 |
  • crosshair(十字)
  • 84 |
  • help(问号)
  • 85 |
  • move(拖拽)
  • 86 |
  • pointer(手)
  • 87 |
  • text(文本框中的鼠标手势)
  • 88 |
89 |

CSS3

90 |

提示禁用状态

91 |
92 |
93 | 94 |
95 |
96 |
97 |
98 | 99 |
100 |
101 |
102 | 131 |

值得注意的是,这个时候这个 a 标签还是可以点击的,只是视觉上改变了而已。如果要做到不可点击,我们可以使用另外一个样式:pointer-events: none,但是这个时候 cursor 样式又不起效了。

132 |

隐藏鼠标光标

133 |
134 |
135 | 136 |
137 |
138 |
139 |
140 | 141 |
142 |
143 |
144 | 177 |

为什么有这个需求?因为有些屏幕并不允许人操作,是只读的。

178 |
179 | 180 | 199 | 200 | -------------------------------------------------------------------------------- /07.结构与布局/36.自适应内部元素.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 78 | 79 | 80 |

难题

81 |
82 |
83 | 84 |
85 |
86 |
87 |
88 | 89 |
90 |
91 |
92 | 127 |

我们希望文字的最大宽度能和图片一样宽,而且 div.wrapper 能水平居中。

128 |

如何让 div.wrapper 的宽度由它内部的图片来决定,而不是由它的父元素来决定呢?

129 |

当开发者走投无路时,就只能对 div.wrapper 应用一个固定的 width 或 max-width,然后对 div.wrapper > img 应用 max-width: 100%。可是这个方法无法充分利用有效空间;对于过小的图片来说,布局效果也很突兀。此外,响应式也无从谈起。

130 |

解决方案

131 |
132 |
133 | 134 |
135 |
136 |
137 |
138 | 139 |
140 |
141 |
142 | 181 |

CSS3 为 width 和 height 属性定义了一些新的关键字,其中最有用的应该就是 min-content 了。这个关键字将解析为这个容器内部最大的不可断行元素的宽度(即最宽的单词、图片或具有固定宽度的盒元素)

182 |
183 | 184 | 203 | 204 | -------------------------------------------------------------------------------- /03.形状/13.梯形标签页.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 78 | 79 | 80 |
81 |
82 | 83 |
84 |
85 |
86 |
87 | 88 |
89 |
90 |
91 | 158 |

这个方法确实可以生成一个基本的梯形,但还有一个问题没有解决。当我们没有设置 transform-origin 属性时,应用变形效果会让这个元素以它自身的中心线为轴进行空间上的旋转。因此,它的宽度会增加,它所占据的位置会稍稍下移,它在高度上会有少许缩减。

159 |

为了让它的尺寸更好掌握,我们可以为它指定 transform-origin: bottom; 当它在 3D 空间中旋转时,可以把它的底边固定住。高度上的缩水,可以通过 scaleY() 来弥补。

160 |
161 |
162 | 163 |
164 |
165 |
166 |
167 | 168 |
169 |
170 |
171 | 240 |
241 | 242 | 261 | 262 | -------------------------------------------------------------------------------- /07.结构与布局/41.紧贴底部的页脚.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 78 | 79 | 80 |
81 |
82 | 83 |
84 |
85 |
86 |
87 | 88 |
89 |
90 |
91 | 172 |

我们所期望的是,页头和页脚的高度由其内部因素来决定,而内容区块的高度应该可以自动伸展并占满所有的可用空间。我们只要给 <main> 这个容器的 flex 属性指定一个大于 0 的值(比如 1),就可以实现这个效果了。

173 |

这个 flex 属性实际上是 flex-grow、flex-shrink 和 flex-basis 的简写语法。任何元素只要设置了一个大于 0 的 flex 值,就将获得可伸缩的特性。flex 的值负责控制多个可伸缩元素之间的尺寸分配比例。

174 |

比如我们让 footer 也自动分配高度,为 main 高度的一半。

175 |
176 |
177 | 178 |
179 |
180 |
181 |
182 | 183 |
184 |
185 |
186 | 267 |
268 | 269 | 288 | 289 | -------------------------------------------------------------------------------- /03.形状/09.自适应的椭圆.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 78 | 79 | 80 |

椭圆

81 |

给元素设置相同宽高以及一半长度的 border-radius,可以得到一个圆形。

82 |

我们期望能达到这个效果:宽高相等,显示为一个圆;宽高不等,显示一个椭圆。

83 |

说到 border-radius,有一个鲜为人知的真相:他可以 单独指定水平和垂直半径,只要用一个斜杠 / 分割这两个值即可。

84 |
85 |
86 | 87 |
88 |
89 |
90 |
91 | 92 |
93 |
94 |
95 | 126 |

半椭圆

127 |

border-radius 对应的各个展开式属性(如果要对其分别设置水平和垂直半径,不需要用 /,空格即可):

128 |
    129 |
  • border-top-left-radius
  • 130 |
  • border-top-right-radius
  • 131 |
  • border-bottom-right-radius
  • 132 |
  • border-bottom-left-radius
  • 133 |
134 |

真正简洁的方法还是使用 border-radius 这个简写属性,我们可以向它一次性提供用空格分开的多个值。当 border-radius 的值为 10px / 5px 20px 时,其效果相当于 10px 10px 10px 10px / 5px 20px 5px 20px。

135 |
136 |
137 | 138 |
139 |
140 |
141 |
142 | 143 |
144 |
145 |
146 | 177 |

四分之一椭圆

178 |

要创建一个四分之一椭圆,其中一个角的水平和垂直半径值都需要是 100%,而其他三个角都不能设为圆角

179 |
180 |
181 | 182 |
183 |
184 |
185 |
186 | 187 |
188 |
189 |
190 | 229 |
230 | 231 | 250 | 251 | -------------------------------------------------------------------------------- /03.形状/10.平行四边形.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 78 | 79 | 80 |

难题

81 |

对元素应用 transform: skewX(45deg) 后,虽然得到了一个平行四边形,但是会导致它的内容也倾向变形。如何使得只让容器的形状倾斜,而保持内容不变?

82 |

嵌套元素方案

83 |

我们可以对内容再应用一次反向的 skew() 变形,从而抵消容器的变形效果。但是我们不得不使用一层额外的 HTML 元素来包裹内容,比如用一个 div:

84 |
85 |
86 | 87 |
88 |
89 |
90 |
91 | 92 |
93 |
94 |
95 | 162 |

伪元素方案

163 |

另一种思路是 把所有样式(背景、边框等)应用到伪元素上,然后再 对伪元素进行变形。因为我们的内容并不是包含在伪元素里的,所以内容并不会受到变形的影响。

164 |
165 |
166 | 167 |
168 |
169 |
170 |
171 | 172 |
173 |
174 |
175 | 256 |

这个技巧不仅对 skew() 变形来说很有用,还适用于其他任何变形样式,当我们想 变形一个元素而不想变形它的内容时 就可以用到它。

257 |

这个技巧的关键在于,我们利用伪元素以及定位属性产生一个方块,然后对伪元素设置样式,并将其放置在其宿主元素的下层。

258 |
259 | 260 | 279 | 280 | -------------------------------------------------------------------------------- /06.用户体验/31.自定义复选框.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 78 | 79 | 80 |

自定义复选框

81 |

<lable> 元素与复选框关联之后,可以起到触发开关的作用。

82 |
83 |
84 | 85 |
86 |
87 |
88 |
89 | 90 |
91 |
92 |
93 | 182 |

开关式按钮

183 |
184 |
185 | 186 |
187 |
188 |
189 |
190 | 191 |
192 |
193 |
194 | 265 |
266 | 267 | 286 | 287 | -------------------------------------------------------------------------------- /03.形状/11.菱形图片.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 78 | 79 | 80 |

基于变形的方案

81 |

先把图片用一个 div 包裹起来,然后对其应用相反的 rotate() 变形样式,并对图片设置 scale 值为根号 2(尝试着修改代码,去掉 scale(1.42))

82 |
83 |
84 | 85 |
86 |
87 |
88 |
89 | 90 |
91 |
92 |
93 | 148 |

但是我们可以发现,图片被放大后,清晰度下降了。所以可以给图片指定小于实际长度的长度,使得其放大的情况下清晰度有所改善。

149 |

注意该方案只适用于正方形图片,不适用于长方形。

150 |

裁剪路径方案

151 |

我们可以使用 polygon()(多边形)函数来指定一个菱形。实际上,它允许我们用一系列(以逗号分隔的)坐标点来指定任意的多边形。我们甚至可以使用百分比值。

152 |
153 |
154 | 155 |
156 |
157 |
158 |
159 | 160 |
161 |
162 |
163 | 202 |

clip-path 甚至可以参与动画,只要我们的动画是在同一种形状函数(比如这里是 polygon())之间进行的,而且点的数量是相同的。因此,如果我们希望图片在鼠标悬停时平滑地扩展为完整的面积,只需要这么做:

203 |
204 |
205 | 206 |
207 |
208 |
209 |
210 | 211 |
212 |
213 |
214 | 261 |

clip-path 方案可以适用于任何矩形,但是浏览器支持有限 http://caniuse.com/#search=clip-path

262 |

推荐一个 clip-path marker http://bennettfeely.com/clippy/

263 |
264 | 265 | 284 | 285 | -------------------------------------------------------------------------------- /02.背景与边框/07.伪随机背景.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 伪随机背景 6 | 7 | 8 | 9 | 10 | 100 | 101 | 102 |

伪随机背景

103 |
104 |
105 | 106 |
107 |
108 |
109 |
110 | 111 |
112 |
113 |
114 | 147 |

可以看到,重复规律非常明显

148 |

为了更真实地模拟条纹的随机性,我们接下来可能会想到,把这组条纹从一个平面拆散为多个图层:一种颜色作为底色,另三种颜色作为条纹,然后再让条纹以不同的间隔进行重复平铺。这一点不难做到,我们在色标中定好条纹的宽度,再用 background-size 来控制条纹的间距

149 |
150 |
151 | 152 |
153 |
154 |
155 |
156 | 157 |
158 |
159 |
160 | 203 |

因为最顶层贴片的重复规律最容易被察觉(它没有被任何东西遮挡),所以我们应该把平铺间距最大的贴片安排在最顶层

204 |

仔细观察,仍然可以看出图像每隔 240px 就会重复一次。其实 240 正是 80 60 40 的最小公倍数(LCM)

205 |

所以,要让这种随机性更加真实,我们只要让最小公倍数最大化,让这些数字最好是 "相对质数" 即可

206 |
207 |
208 | 209 |
210 |
211 |
212 |
213 | 214 |
215 |
216 |
217 | 260 |
261 | 262 | 263 | 264 | 283 | 284 | -------------------------------------------------------------------------------- /07.结构与布局/39.满幅的背景,定宽的内容.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 78 | 79 | 80 |

满幅的背景,定宽的内容,这个需求很常见。简单说其实就类似块级元素的水平居中。

81 |

常见的做法:

82 |
83 |
84 | 85 |
86 |
87 |
88 |
89 | 90 |
91 |
92 |
93 | 142 |

margin: auto 发挥了什么作用?这条声明所产生的左右外边距实际上等于视口宽度的一半减去内容宽度的一半。我们可以把这个外边距的值表达为 50% - 150px。

143 |
144 |
145 | 146 |
147 |
148 |
149 |
150 | 151 |
152 |
153 |
154 | 203 |

我们可以进一步把这个长度值应用到父元素的 padding 上:

204 |
205 |
206 | 207 |
208 |
209 |
210 |
211 | 212 |
213 |
214 |
215 | 264 |

我们还可以把 max-width 一并去掉:

265 |
266 |
267 | 268 |
269 |
270 |
271 |
272 | 273 |
274 |
275 |
276 | 323 |
324 | 325 | 344 | 345 | -------------------------------------------------------------------------------- /02.背景与边框/04.边框内圆角.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 边框内圆角 6 | 7 | 8 | 9 | 10 | 100 | 101 | 102 |

边框内圆角

103 |

难题

104 |

有时候我们需要一个容器,只在内侧有圆角,而边框或描边的四个角在外部仍然保持直角的形状

105 |

两个元素嵌套

106 |

用两个元素嵌套很容易实现这个效果:

107 |
108 |
109 | 110 |
111 |
112 |
113 |
114 | 115 |
116 |
117 |
118 | 167 |

更优雅的方案

168 |
169 |
170 | 171 |
172 |
173 |
174 |
175 | 176 |
177 |
178 |
179 | 224 |

我们基本受益于两个事实,描边(outline)并不会跟着元素的圆角走(因而显示出直角),但是 box-shadow 却是会的。因此,如果我们把这两者叠加到一起,box-shadow 会刚好填补描边和容器圆角之间的空隙。

225 |

事实上,指定一个等于描边宽度的扩张值可能会得到异常渲染(目前在 chrome 71 中也是一样,以上 demo 仔细看),因此推荐一个更小的值,那么,这个值是?

226 |

事实上,我们需要满足 \((\sqrt2-1)*r\)(r 表示 border-radius)< 扩张半径(box-shadow) < 描边宽度(outline)(但是,扩张半径需要比描边宽度至少小多少呢?我测试了下,可能并没有准确的答案),这意味着,如果描边的宽度比 \((\sqrt2-1)*r\) 小,那我们是不可能用这个方法达成效果。\((\sqrt2-1)\) 约等于 0.414,如果可以的话,我们可以将扩张半径取值为 border-radius 的一半

227 |
228 |
229 | 230 |
231 |
232 |
233 |
234 | 235 |
236 |
237 |
238 | 283 |

我们再次强调一遍,该方案的实现完全依赖于 描边不跟着圆角走这个事实,但是未来的 CSS 规范明确地建议描边跟着圆角走,我们拭目以待

284 |
285 | 286 | 287 | 288 | 307 | 308 | -------------------------------------------------------------------------------- /06.用户体验/30.扩大可点击区域.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 78 | 79 | 80 |

一个简单的 demo:

81 |
82 |
83 | 84 |
85 |
86 |
87 |
88 | 89 |
90 |
91 |
92 | 137 |

扩张热区最简单的方法是为它设置一圈透明框,因为鼠标对元素边框的交互也会触发鼠标事件

138 |
139 |
140 | 141 |
142 |
143 |
144 |
145 | 146 |
147 |
148 |
149 | 200 |

但是,按钮同时也变大了!原因在于背景在默认情况下会蔓延到边框的下层。

201 |

background-clip 属性可以把背景限制在原本的区域之内。

202 |
203 |
204 | 205 |
206 |
207 |
208 |
209 | 210 |
211 |
212 |
213 | 266 |

但是 hover 的时候 background-clip 并不起效(我怀疑是个 bug @2017.08.28),对于普通的按钮还好,如果是 demo 里这样的需求,非常不友好;而且这个时候如果需要添加真正的边框,就只能用 box-shadow 去模拟了。而且,只能用内嵌投影,外部投影会作用到透明的边框的外面。

267 |

我们放弃边框,然后改用另外一个特性来实现:伪元素同样可以代表其宿主元素来响应鼠标交互。神奇的伪元素!

268 |
269 |
270 | 271 |
272 |
273 |
274 |
275 | 276 |
277 |
278 |
279 | 344 |

这个方法的优点是,我们基本上可以把热区设置为任何想要的尺寸、位置或者形状,甚至可以脱离元素原有的位置!

345 |
346 | 347 | 366 | 367 | -------------------------------------------------------------------------------- /07.结构与布局/38.根据兄弟元素的数量来设置样式.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 78 | 79 | 80 |

难题

81 |

对于 CSS 选择符来说,基于兄弟元素的总数来匹配元素并不简单。设想一个列表,假设 仅当列表项的总数为 4 时 才对这些列表项设置样式。我们可以用 li:nth-child(4) 来选中列表的第四个列表项,但这并不是我们想要的;我们需要 在列表项的总数为 4 时 选中 每一个 列表项。

82 |

解决方案

83 |
84 |
85 | 86 |
87 |
88 |
89 |
90 | 91 |
92 |
93 |
94 | 149 |

这个方法需要的代码还是冗长繁琐的,我们可以用预处理器优化。

150 |

根据兄弟元素的数量范围来匹配元素

151 |

列表项的总数是 4 或者更多时选中所有列表项:

152 |
153 |
154 | 155 |
156 |
157 |
158 |
159 | 160 |
161 |
162 |
163 | 216 |

列表项的总数是 4 或者更少时,选中所有列表项:

217 |
218 |
219 | 220 |
221 |
222 |
223 |
224 | 225 |
226 |
227 |
228 | 281 |

我们也可以把两种技巧组合起来使用,假设我们希望在列表包含 2 ~ 6 个列表项时命中所有的列表项。

282 |
283 |
284 | 285 |
286 |
287 |
288 |
289 | 290 |
291 |
292 |
293 | 352 |
353 | 354 | 373 | 374 | -------------------------------------------------------------------------------- /02.背景与边框/02.多重边框.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 多重边框 6 | 7 | 8 | 9 | 10 | 100 | 101 | 102 |

多重边框

103 |

难题

104 |

众所周中,border 可以设置边框,但是有时我们需要多重边框呢?

105 |

ugly 方案

106 |

一个很容易想到的方案是多层元素嵌套,这个方案非常不优雅。

107 |

box-shadow 方案

108 |

box-shadow 接受第四个参数(扩张半径),通过指定正值或者负值,可以让投影面积增大或者减小。一个正值的扩张半径加上两个为零的偏移量以及为零的模糊值,得到的 “投影” 其实就像一道实线边框。

109 |

同时,box-shadow 支持逗号分割语法,我们可以创建任意数量的投影。需要注意的是,box-shadow 是层层叠加的,第一层投影位于最顶层,依次类推

110 |
111 |
112 | 113 |
114 |
115 |
116 |
117 | 118 |
119 |
120 |
121 | 170 |

需要注意的点:

171 |
    172 |
  • 投影的行为和边框不完全一致。因为它不影响布局,而且也不会受到 box-sizing 属性的影响。不过,你还是可以通过内边距或者外边距(这取决于投影是内嵌还是外扩的)来额外模拟出边框所需要占据的空间(参考上面的 demo 给 body 加了 padding,不然投影就会到视野外了)
  • 173 |
  • 投影创造的假边框出现在元素的 外圈。它们不会影响鼠标事件,如果这一点非常重要,你可以给 box-shadow 属性加上 inset 关键字,来使投影绘制在元素的 内圈。注意此时需要额外增加内边距来腾出足够的空隙(注意这时候颜色顺序也要跟之前相反,数值也要重新计算过
  • 174 |
175 |
176 |
177 | 178 |
179 |
180 |
181 |
182 | 183 |
184 |
185 |
186 | 225 |

outline 方案

226 |

在某些情况下,你可能只需要两层边框。可以先设置一层常规边框,再加上 outline(描边)属性来产生外层的边框。这种方法的一大优势在于边框样式十分灵活

227 |
228 |
229 | 230 |
231 |
232 |
233 |
234 | 235 |
236 |
237 |
238 | 285 |

描边的另外一个好处在于,你可以通过 outline-offset 属性来控制它跟元素边缘之间的间距,这个属性甚至可以 接受负值

286 |

我们可以实现一个简单的缝边效果:

287 |
288 |
289 | 290 |
291 |
292 |
293 |
294 | 295 |
296 |
297 |
298 | 337 |
338 | 339 | 340 | 341 | 360 | 361 | -------------------------------------------------------------------------------- /02.背景与边框/03.灵活的背景定位.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 灵活的背景定位 6 | 7 | 8 | 9 | 10 | 100 | 101 | 102 |

灵活的背景定位

103 |

难题

104 |

很多时候,我们想针对容器某个角对背景图片做偏移定位,如右下角。在 CSS 2.1 中,我们只能指定距离左上角的偏移量,或者干脆完全靠齐到其他三个角。但是,我们有时希望图片和容器的边角之间能留出一定的空隙(类似内边距的效果)

105 |

对于具有固定尺寸的容器来说,使用 CSS 2.1 来做到这一点是可能的,但很麻烦:可以基于它自身的尺寸以及我们期望它距离右下角的偏移量,计算出背景图片距离左上角的偏移量,然后再把计算结果设置给 background-position。当容器元素尺寸不固定时(因为内容往往是可变的),这就不可能做到了。开发者通常只能把 background-position 设置为某个接近 100% 的百分比值

106 |

所以我们的挑战是,把背景图片定位到距离底边 10px 且距离右边 20px 的位置

107 |

background-position 的扩展语法方案

108 |

在 CSS3 中,background-position 属性已经得到扩展,它允许我们指定背景图片 距离任意角的偏移量,只要我们 在偏移量前面指定关键字

109 |
110 |
111 | 112 |
113 |
114 |
115 |
116 | 117 |
118 |
119 |
120 | 157 |

background-origin 方案

158 |

在给背景图片设置距离某个角的偏移量时,有一种情况极其常见:偏移量和容器的内边距一致。如果采用上面提到的 background-position 的扩展语法方案,代码如下:

159 |
160 |
161 | 162 |
163 |
164 |
165 |
166 | 167 |
168 |
169 |
170 | 207 |

代码不够优雅,如需改动需要在三个地方更新值

208 |

还有一个更简单的方法可以实现这个需求,让它自动跟着我们设定的内边距走,不需要另外声明偏移的量

209 |
210 |
211 | 212 |
213 |
214 |
215 |
216 | 217 |
218 |
219 |
220 | 257 |

默认情况下,background-position 是以 padding box 为准的,这样边框才不会遮住背景图片。因此,top left 默认指的是 padding box 的左上角。background-origin 默认取值 padding-box,还可以取值 content-box,border-box

258 |

calc() 方案

259 |

如果我们仍然 以左上角偏移的思路 来考虑,其实就是希望它有一个 100% - 20px 的水平偏移量,以及 100% - 10px 的垂直偏移量。calc() 可以做到这点(其实这里 calc() 为啥这样能实现效果我不是很清楚,为啥 background-position: calc(100%) calc(100%) 背景图就能去右下角了?)

260 |
261 |
262 | 263 |
264 |
265 |
266 |
267 | 268 |
269 |
270 |
271 | 306 |
307 | 308 | 309 | 310 | 329 | 330 | -------------------------------------------------------------------------------- /02.背景与边框/01.半透明边框.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 半透明边框 6 | 7 | 8 | 9 | 10 | 100 | 101 | 102 |

半透明边框

103 |

难题

104 |
105 |
106 | 107 |
108 |
109 |
110 |
111 | 112 |
113 |
114 |
115 | 182 |

demo 很简单,白色背景的 div 大小 50*50px,然后给他加了 10px 的半透明边框,我们希望背景图能够透过来,但是事与愿违,我们看到的白色矩形实际上是 70*70px。这是因为 半透明边框透出了这个容器自己的纯白色背景

183 |

默认状态下,背景会延伸到边框的区域下层:

184 |
185 |
186 | 187 |
188 |
189 |
190 |
191 | 192 |
193 |
194 |
195 | 230 |

这里的理解我之前有个误区,并不是边框设置了带透明度的颜色,背景就会透上来,而是背景和边框用了一个颜色,然后边框又设置了透明度,背景才会透上来。比如上面的 demo 我边框换个颜色 rgba(125, 255, 255, .5),就没有什么问题了(所以感觉实际生产中很少碰到这个问题)

231 |

解决方案

232 |

CSS3 起,我们可以通过设置 background-clip 属性来调整上述默认行为所带来的不便。这个属性的初始值是 border-box,意味着 背景会被元素的 border box(边框的外沿框)裁减掉。如果不希望背景侵入边框所在的范围,我们要做的就是把它的值设为 padding-box。

233 |
234 |
235 | 236 |
237 |
238 |
239 |
240 | 241 |
242 |
243 |
244 | 281 |
282 |
283 | 284 |
285 |
286 |
287 |
288 | 289 |
290 |
291 |
292 | 361 |
362 | 363 | 364 | 365 | 384 | 385 | -------------------------------------------------------------------------------- /04.视觉效果/15.单侧投影.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 78 | 79 | 80 |

box-shadow

81 |

很多人使用 box-shadow 的方法是,指定三个长度值和一个颜色值。

82 |
83 |
84 | 85 |
86 |
87 |
88 |
89 | 90 |
91 |
92 |
93 | 128 |
    129 |
  1. 以该元素相同的尺寸和位置,画一个背景色为 black 的矩形
  2. 130 |
  3. 把它向右移 5px,向下移 4px
  4. 131 |
  5. 使用高斯模糊算法(或类似算法)将它进行 4px 的模糊处理。这在本质上表示在阴影边缘发生阴影色和纯透明色之间的颜色过渡长度近似于模糊半径的两倍(比如这里是 8px)
  6. 132 |
  7. 模糊后的矩形与原始元素的交集部分会被切除掉
  8. 133 |
134 |

但是这在某种程度上会导致外露的投影太过浓重,看起来不是很美观。

135 |

单侧投影

136 |

解决方案来自 box-shadow 鲜为人知的第四个长度参数,称为扩张半径。这个值会根据你指定的值去扩大或(当指定负值)缩小投影的尺寸。

137 |

从逻辑上来讲,如果我们应用一个负的扩张半径,而它的值刚好等于模糊半径,那么投影的尺寸就会和投影所属元素的尺寸完全一致。除非用偏移量来移动它,否则我们将看不到任何投影

138 |
139 |
140 | 141 |
142 |
143 |
144 |
145 | 146 |
147 |
148 |
149 | 202 |

因此,我们给投影应用一个正的垂直偏移量即可。

203 |
204 |
205 | 206 |
207 |
208 |
209 |
210 | 211 |
212 |
213 |
214 | 255 |

邻边投影

256 |

传统的应用 box-shadow 会使得阴影太过浓厚。

257 |
258 |
259 | 260 |
261 |
262 |
263 |
264 | 265 |
266 |
267 |
268 | 309 |

我们可以这样做(将扩张半径设置为模糊半径相反值的一半):

310 |
311 |
312 | 313 |
314 |
315 |
316 |
317 | 318 |
319 |
320 |
321 | 362 |

双侧阴影

363 |

把单侧投影中的技巧应用两次。

364 |
365 |
366 | 367 |
368 |
369 |
370 |
371 | 372 |
373 |
374 |
375 | 418 |
419 | 420 | 439 | 440 | --------------------------------------------------------------------------------