CSS Basic Notes
CSS Working Group
- CSS working group: CSS WG.
- W3C standard types:
- ED:
Editor's Draft
. - FPWD:
First Public Working Draft
. - WD:
Working Draft
. - CR:
Candidate Recommendation
. - PR:
Proposed Recommendation
. - REC: a W3C
Recommendation
is a W3C Technical Report.
- ED:
CSS Cascading and Inheritance
Cascading Order
- Inherit styles.
- User agent normal styles.
- User normal styles.
- Author
@layer
normal styles. - Author normal styles.
- Animation styles.
- Author
!important
styles. - Author
@layer
!important
styles. - User
!important
styles. - User agent
!important
styles. - Transition styles.
Transition > Animation > Normal >
@layer
> User > User Agent > Inherit
- 级联水平高的 styles 应用 !important 后, 其优先级变低.
- 级联水平低的 styles 应用 !important 后, 其优先级变高.
Layer
Layer Formal Syntax
@layer
formal syntax:
@layer [<layer-name># | <layer-name>? {
<stylesheet>
}];
@layer base;
@layer theme, layout, components, utilities;
@layer base {
html {
font-size: 1rem;
}
}
@layer {
html {
font-size: 1rem;
}
}
@layer reset, externals, base, components, utilities;
@import url('reset.css') layer(reset);
@import url('carousel.css') layer(externals);
@import url('map.css') layer(externals);
<link rel="stylesheet" href="reset.css" layer="reset" media="supports(at-rule(@layer))" />
Layer Specificity
/* utilities > components > layout > theme */
@layer theme, layout, components, utilities;
/* c > c.d > a > a.b */
@layer a {
p {
color: red;
}
@layer b {
p {
color: green;
}
}
}
@layer c {
p {
color: orange;
}
@layer d {
p {
color: blue;
}
}
}
Scope
- 局部上下文: 生效元素必须为
@scope
的子元素 - 规则完整性: 规则中的每一行代码, 必须属于一个完整的规则, 不能是一条单独的 CSS 声明.
<section class="scope-root">
<h3>Scope Root</h3>
<p>Paragraph 1</p>
<!-- Selected -->
<img src="image.jpg" alt="Image" />
<p>Paragraph 2</p>
<figure class="scope-limit">
<!-- Not selected -->
<img src="image.jpg" alt="Image" />
<figcaption>Figure Caption</figcaption>
</figure>
</section>
@scope (.scope-root) to (.scope-limit) {
img {
background-color: red;
}
& img {
background-color: red;
}
:scope img {
background-color: red;
}
}
Nesting
ul {
& + & {
font-weight: bold;
color: red;
}
}
Specificity
Specificity
(Selector Priority
) has 4 bits,
thousands, hundreds, tens, ones 0000
:
- Thousands: inline-style.
- Hundreds: ID selector (实际开发中一般用
[id="Id"]
代替优先级过高的 ID selector). - Tens: class selector, attribute selector, pseudo class(
:
). - Ones: type selector, pseudo element(
::
).
- Universal selector (
*
), combinators (+
,>
,~
,a b
) and:where()
have no effect on specificity. :not()
/:is()
/:has()
have no effect on specificity, but selectors in it have effect on specificity.
<!-- specificity: 1000 -->
<h1 style="color: black">Hello</h1>
/* specificity: 0001 */
h1 {
color: red;
}
/* specificity: 0100 */
#id {
color: green;
}
/* specificity: 0003 */
h1 + p::first-letter {
color: blue;
}
/* specificity: 0022 */
li > a[href*='link-url'] > .inline-warning {
color: yellow;
}
/* specificity: 0023 */
div li:nth-child(2) a:hover,
div li:nth-child(2) a:focus {
border: 10px dashed black;
}
/* specificity: 0024 */
div div li:nth-child(2) a:hover,
div div li:nth-child(2) a:focus {
border: 10px solid black;
}
/* specificity: 0033 */
div div .nav:nth-child(2) a:hover,
div div .nav:nth-child(2) a:focus {
border: 10px double black;
}
/* specificity: 0101 */
#outer a {
background-color: red;
}
/* specificity: 0104 */
#outer div ul li a {
color: yellow;
}
/* specificity: 0113 */
#outer div ul .nav a {
color: white;
}
/* specificity: 0201 */
#outer #inner a {
background-color: blue;
}
Styles for a directly targeted element will always take precedence over inherited styles, regardless of the specificity of the inherited rule:
#parent {
color: green;
}
/* <h1> element will be purple */
h1 {
color: purple;
}
Increasing specificity by duplicating selector:
.my-class.my-class.my-class span {
/* 0-3-1 */
color: white;
}
:is(.my-class.my-class.my-class, span) {
/* 0-3-0 */
color: white;
}
Inheritance
- Most CSS properties that affect the text node are inherited properties: color, font-size, font-family, etc.
- Most CSS properties that affect the element node are non-inherited properties.
- When the
unset
value is set on an inherited property, it resets the property value to its inherited value. unset
value resets a non-inherited property to itsinitial
value.revert
reverses the CSS default values to the browser user-agent styles.
Inheritable CSS Property
- visibility
- cursor
- color
- direction
- font-family
- font-size
- font-style
- font-variant
- font-weight
- font
- line-height
- letter-spacing
- word-spacing
- white-space
- text-align
- text-indent
- text-transform
- border-collapse
- border-spacing
- caption-side
- empty-cells
- list-style-image
- list-style-position
- list-style-type
- list-style
- orphans
- quotes
- widows
CSS Selectors
Universal Selector
*
:
- 不影响选择器优先级.
- 匹配自定义元素,
<script>
,<style>
,<title>
. - 不匹配伪元素.
Type Selector
p {
margin-bottom: 1em;
line-height: 1.5em;
}
Attribute Selector
E[attr]
:
/* 定位页面里所有具有必填属性 "required" 的 input */
input[required] {
border: 1px solid #f00;
}
E[attr=value]
match value
:
/* 定位页面里的密码输入框 */
input[type='password'] {
border: 1px solid #aaa;
}
E[attr|=value]
match value
/value-
:
/**
* 定位页面里所有的 pre 里具有 class 属性且属性值为 language 或是 language- 开头的
* 比如 class="language", class="language-tsx"
*/
pre[class|='language'] {
color: #333;
}
E[attr~=value]
match value
/* value *
:
/**
* 定位页面里所有具有属性 title 且属性值里拥有完整单词 english 的 div 容器
* 比如 title="english", title="a english"
*/
div[title~='english'] {
color: #f88;
}
E[attr^=value]
match ^value
:
/**
* 定位页面里具有属性 class 且属性值以 a 开头的 div 容器
* 比如 class="a", class="ab"
*/
div[class^='a'] {
color: #666;
}
E[attr$=value]
match value$
:
/**
* 定位页面里具有属性 class 且属性值以 a 结尾的 div 容器
* 比如 class="nba", class="cba"
*/
div[class$='a'] {
color: #f00;
}
E[attr*=value]
match *value*
:
/* 定位所有 title 里具有 link 字符串的 a 链接 */
a[title*='link'] {
text-decoration: underline;
}
Descendant Combinator
E F
后代选择器:
ul li {
margin-bottom: 0.5em;
}
Using the descendant selector without more specificity can be really expensive. The browser is going to check every descendant element for a match because the relationship isn't restricted to parent and child.
For .container ul li a
selector:
- match every
<a>
on the page - find every
<a>
contained in a<li>
- use the previous matches and narrow down to
the ones contained in a
<ul>
- finally, filter down the above selection to
the ones contained in an element with the class
.container
Child Combinator
E > F
子代选择器:
ul > li {
list-style: none;
} /* 仅限ul的直接子元素li, 忽略嵌套子元素 */
General Sibling Combinator
E ~ F
一般 兄弟选择器:
/* p before h1 */
p {
color: #fff;
}
/* 定位具有相同父元素的, h1标签之后的所有p标签 */
h1 ~ p {
color: #f00;
}
Checkbox input
as hidden click
event listener:
input.checkbox {
visibility: hidden;
opacity: 0;
}
nav {
transform: scale(0);
}
input.checkbox:checked ~ nav {
transform: scale(1);
}
Adjacent Sibling Combinator
E + F
相邻兄弟选择器:
* + * {
margin-top: 1.5em;
}
li + li {
border-top: 1px solid #ddd;
}
Location Pseudo Class
Link Pseudo Class
:link
:
- 只匹配未访问的
<a href>
. - 可用
a
/[href]
选择器代替.
Visited Pseudo Class
:visited
:
- 只匹配访问过的
<a href>
. - 只支持设置颜色:
color
/background-color
/outline-color
/border-color
/column-rule-color
/text-decoration-color
. - 不支持颜色透明度 (
alpha
). - 只支持重置已有颜色, 不能新增设置样式.
window.getComputedStyle
无法获取到:visited
设置颜色.
Any Link Pseudo Class
:any-link
:
- 同时匹配
:link
与:visited
元素. - 匹配所有设置了
[href]
的链接元素:<a href>
/<link href>
/<area href>
.
Target Pseudo Class
:target
:
- 该选择器定位当前活动页面内定位点的目标元素 (#anchor-name)
#info:target {font-size:24px;}
. - 可用于实现 tab/modal/carousel/gallery/slide:
- 利用
display:none
隐藏#id
元素, 不会触发页面滚动 (防止页面抖动), 可以触发:target
伪类匹配. :target ~ .content
控制实际内容切换.
- 利用
<a href="#p1">p1</a>
<div id="p1">p1</div>
<style>
div:target {
background-color: purple;
}
</style>
.anchor {
display: none;
}
.content {
max-height: 0;
}
.anchor:target ~ .content {
max-height: 100%;
}
:target-within
:
- Selected when any children targeted.
User Action Pseudo Class
Hover Pseudo Class
- 鼠标移动到容器时的状态.
- 不仅限于链接, 可用于页面中的任何元素.
Active Pseudo Class
:active
:
- 点击 (mouse click/screen touch) 时的状态.
- 键盘访问无法激活
:active
. - 不仅限于链接, 可用于任何具有
tabindex
属性的元素.
:link
—> :visited
—> :hover
—> :active
links:
/* Unvisited links */
a:link {
color: blue;
}
/* Visited links */
a:visited {
color: purple;
}
/* Hovered links */
a:hover {
background: yellow;
}
/* Active links */
a:active {
color: red;
}
[href]:active,
button:active,
[type='button']:active,
[type='reset']:active,
[type='submit']:active {
clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);
background: linear-gradient(rgb(0 0 0 / 5%), rgb(0 0 0 / 5%));
outline: 999px solid rgb(0 0 0 / 5%);
outline-offset: -999px;
box-shadow: inset 0 0 0 999px rgb(0 0 0 / 5%);
}
Focus Pseudo Class
:focus
:
- 获得焦点时的状态 (包括键盘访问).
- 不仅限于
<a href>
/<button>
/<input>
/<select>
/<area>
/<summary>
, 可用于任何具有tabindex
/contenteditable
属性的元素.
:focus-visible
:
- Selected when
Tab
(keyboard) focused. - 可用于区分鼠标与键盘激活样式.
Separate focus styles:
/* Tab Focus Style */
.button:focus-visible {
outline: 2px solid #416dea;
outline-offset: 2px;
box-shadow: 0 1px 1px #416dea;
}
/* Mouse Focus Style */
.button:focus:not(:focus-visible) {
outline: none;
}
:focus-within
:
- Selected when any children focused.
- 可用于实现
dropdown
.
.dropdown-list {
display: none;
}
.dropdown:focus-within .dropdown-list {
display: block;
}
Input Pseudo Class
:autofill
.:enabled
: 匹配启用的界面元素, e.ginput
.:disabled
: 匹配禁用的界面元素 ([disabled]
), e.ginput
.:read-only
: 匹配其内容无法供用户修改的元素 (<div>
/[readonly]
).:read-write
: 匹配其内容可供用户修改的元素 (<div contenteditable>
/<input>
).:default
: 匹配处于默认状态的表单元素, 可用于默认选项/推荐选项样式.:checked
: 匹配处于选中状态的表单元素, 可用于开关选框/多选框样式, e.g tab, dropdown, modal, carousel, tree, checkbox grid.:indeterminate
:- 匹配处于未选状态的单选框元素
<input type="radio">
. - 匹配处于半选状态的复选框元素
<input type="checkbox">
. - 匹配处于未设置
value
的进度条元素<progress>
.
- 匹配处于未选状态的单选框元素
:valid
: 匹配输入验证有效的表单元素 (<input type>
/<input pattern>
).:invalid
: 匹配输入验证无效的表单元素.:user-invalid
: 匹配用户交互后仍然验证无效的表单元素.:in-range
: 匹配具有范围限制的元素, 其中该值位于限制范围内, e.g 具有min
和max
属性的number
和range
输入框.:out-of-range
: 与:in-range
选择相反, 其中该值位于限制范围外.:required
: 匹配具有必填属性[required]
的表单元素.:optional
: 匹配没有必填属性[required]
的表单元素.:placeholder-shown
: selectinput
with placeholder, 可用于控制输入样式.
@media only screen and (prefers-reduced-motion: reduce) {
.msg {
transition: none;
}
}
.msg {
opacity: 0;
transition: opacity 0.2s ease-in-out;
}
.input:not(:placeholder-shown) ~ .label,
.input:focus ~ .label {
opacity: 1;
}
Structural Pseudo Class
:root
:- 根元素, 始终指 html 元素.
:root
选择器优先级高于html
选择器.- 为了代码可读性,
:root
用于设置全局变量,html
用于设置全局样式.
:empty
: 没有任何子元素的元素, 不能有注释节点与文本节点.E F:nth-child(n)
:该选择器定位元素 E 的第 n 个子元素的元素 F,可省略 E.E F:nth-last-child(n)
: 该选择器定位元素 E 的倒数第 n 个子元素的元素 F,可省略 E.E F:first-child
: 第一个孩子.E F:last-child
: 最后一个孩子.E F:only-child
: 单一后代.E F:nth-of-type(n)
: 该选择器定位元素 E 的第 n 个 相同类型 子元素,可省略 E.E F:nth-lash-of-type(n)
: 该选择器定位元素 E 的导数第 n 个 相同类型 子元素,可省略 E.E F:first-of-type
: 相同类型 的第一个元素.E F:last-of-type
: 相同类型 的最后一个元素.E F:only-of-type
: 孩子中只有一种该元素.
n
start from 0
,
calculation result limit to > 0
:
:nth-child(5n)
:0, 5, 10, 15, ...
->5, 10, 15, ...
.:nth-child(3n + 4)
:4, 7, 10, 13, ...
->4, 7, 10, 13, ...
.:nth-child(-n + 3)
:3, 2, 1, 0, -1, ...
->3, 2, 1
.:nth-child(n + 4):nth-child(-n + 10)
: 两个n
分开计算,4, 5, 6, ...
+10, 9, 8, ...
->4, 5, 6, 7, 8, 9, 10
.
li:first-child:nth-last-child(4),
li:first-child:nth-last-child(4) ~ li {
/* 当列表正好包含 4 项时, 命中所有列表项 */
color: darkblue;
}
/* stylelint-disable-next-line no-descending-specificity */
li:first-child:nth-last-child(n + 4),
li:first-child:nth-last-child(n + 4) ~ li {
/* 当列表至少包含 4 项时, 命中所有列表项 */
color: darkblue;
}
li:first-child:nth-last-child(n + 2):nth-last-child(-n + 6),
li:first-child:nth-last-child(n + 2):nth-last-child(-n + 6) ~ li {
/* 当列表包含 2 ~ 6 项时, 命中所有列表项 */
color: darkblue;
}
Logical Pseudo Class
:not(<selector>)
:- Selector priority.
- 选择与括号内的选择器不匹配的元素.
:is(<selector>)
:- Selector priority.
- Legacy name:
:any()
/:matches()
.
:where(<selector>)
:0
priority.
<target>:has(<selector>)
:- Selector priority.
- A target element has child elements:
:has(> selector)
. - A target element has sibling elements:
:has(+ selector)
.
:is(ol, ul) :is(ol, ul) li {
margin-left: 2rem;
}
Use :has()
selector for conditional styling:
:root {
&:has(.size-selector [value='xs']:checked) {
--font-size: 0.8em;
}
&:has(.size-selector [value='s']:checked) {
--font-size: 0.9em;
}
&:has(.size-selector [value='m']:checked) {
--font-size: 1em;
}
&:has(.size-selector [value='l']:checked) {
--font-size: 1.2em;
}
&:has(.size-selector [value='xl']:checked) {
--font-size: 1.5em;
}
&:has(.theme-selector [value='dark']:checked) {
--background: #000;
--color: #fff;
--line-color: hotpink;
}
&:has(.theme-selector [value='light']:checked) {
--background: #ddd;
--color: #000;
--line-color: darkgoldenrod;
--button-background: #0001;
}
&:has(.theme-selector [value='colorful']:checked) {
--background: linear-gradient(60deg, maroon, darkblue);
--color: #aff;
--line-color: #f55;
--content-background: #0004;
}
}
body {
display: grid;
place-items: center;
min-height: 100vh;
margin: 0;
font-size: var(--font-size, 1em);
color: var(--color);
background: var(--background);
}
h1 {
text-decoration: underline;
text-decoration-color: var(--line-color, currentcolor);
}
button {
padding: 1em 2em;
color: var(--color);
background-color: var(--button-background, #fff3);
border: 2px solid var(--line-color, currentcolor);
}
section {
width: 100%;
max-width: 50ch;
padding: 2em;
background-color: var(--content-background, none);
}
Linguistic Pseudo Class
:dir(ltr)
/:dir(rtl)
.:lang(en)
: 具有使用双字母缩写 (en
) 表示的语言的元素.
:lang(en) > q {
quotes: '\201C' '\201D' '\2018' '\2019';
}
:lang(fr) > q {
quotes: '<< ' ' >>';
}
:lang(de) > q {
quotes: '>>' '<<' '\2039' '\203A';
}
Misc Pseudo Class
:fullscreen
.
First Letter and Line Pseudo Element
::first-letter
/::first-line
:
::first-letter
: 匹配文本首字母.::first-line
: 匹配文本首行.- IE9 及以上版本浏览器支持双冒号, IE8 浏览器只支持单冒号写法.
- 只作用于块级元素:
display
block
/inline-block
/list-item
/table-cell
/table-caption
. - 只支持部分 CSS 属性:
color
.font
properties:font-size
,font-weight
.text
properties:text-decoration
,word-spacing
.background
properties:background-color
,background-image
.border
properties:border-color
.float
.
Selection Pseudo Element
::selection
匹配突出显示的文本:
color
.background-color
.cursor
.caret-color
.outline
.text-decoration
.text-emphasis-color
.text-shadow
.stroke-color
.fill-color
.stroke-width
.
/* 定义选中的文本颜色与背景色 */
::selection {
color: #fff;
background: #444;
}
Target Text Pseudo Element
::target-text {
color: white;
background-color: rebeccapurple;
}
Before and After Pseudo Element
使用 content
属性生成额外的内容并插入在标记中:
a::after {
content: '↗';
}
attr()
, 调用当前元素的属性:
a::after {
content: '(' attr(href) ')';
}
b::after {
content: '(' attr(data-language) ')';
}
url()
/uri()
, 用于引用媒体文件:
h1::before {
content: url('logo.png');
}
counter()
, 调用计数器, 可以不使用列表元素实现序号功能,
配合 CSS3 中counter-increment
和counter-reset
属性:
<div>
<h2>HTML</h2>
<h2>CSS</h2>
<h2>JS</h2>
</div>
<style>
div {
counter-reset: tidbit-counter 58;
}
h2::before {
content: counter(tidbit-counter, list-style-type) ': ';
counter-increment: tidbit-counter 1;
}
</style>
<!-- output
59: HTML
60: CSS
61: JS
output -->
伪元素可用于扩大可点击区域:
.btn-text::before {
position: absolute;
inset: -6px -8px;
content: '';
}
Backdrop Pseudo Element
/* Backdrop is only displayed when dialog is opened with dialog.showModal() */
dialog::backdrop {
background: rgb(255 0 0 / 25%);
}
video::backdrop {
background-color: #448;
}
Marker Pseudo Element
animation-*
.transition-*
.color
.direction
.font-*
.content
.unicode-bidi
.white-space
.
li::marker {
font-variant-numeric: tabular-nums;
text-align: start;
text-align-last: start;
text-indent: 0;
text-transform: none;
unicode-bidi: isolate;
}
Shadow DOM Pseudo Class and Element
:host
: shadow DOM root element.:host-context
: shadow DOM root parent element.::part()
.::slotted()
.
Focusable Selector
const FOCUSABLE_SELECTOR = [
'[contenteditable]',
'[tabindex="0"]:not([disabled])',
'a[href]',
'audio[controls]',
'button:not([disabled])',
'iframe',
'input:not([disabled]):not([type="hidden"])',
'select:not([disabled])',
'summary',
'textarea:not([disabled])',
'video[controls]',
].join(',')
CSS Data Types
CSS data types define typical values (including keywords and units) accepted by CSS properties and functions:
- Textual data types.
- Numeric data types.
- Quantities.
- Combinations of types.
- Color data types.
- Image data types.
- 2D
<position>
.
CSS data types list:
- CSS formal syntax.
- CSS values.
- CSS units.
- CSS functions.
CSS Property Values
Inherit Value
Inherit from parent.
Initial Value
The initial value of a CSS property is its default value, as listed in its standard definition table.
Revert Value
Revert to user agent built in styles.
@supports (-webkit-overflow-scrolling: touch) {
progress {
all: revert;
}
}
Unset Value
Reset to inherit
or initial
value.
dialog {
all: unset; /* Exclude `unicode-bidi`, `direction`, custom variables */
}
Specified Value
The specified value of a CSS property is the value it receives from the document's style sheet
Computed Value
The computed value of a CSS property is the value that is transferred from parent to child during inheritance. It is calculated from the specified value by:
- Handling the special values
inherit
,initial
,unset
, andrevert
- Doing the computation needed to reach the value described in the "Computed value" line in the property's definition table
span {
/* display computed to `block` */
position: absolute;
}
Used Value
The used value of a CSS property is its value after all calculations have been performed on the computed value:
- The used values of dimensions (e.g., width, line-height) are in pixels
- The used values of shorthand properties (e.g., background) are consistent with those of their component properties (e.g., background-color or background-size) and with position and float
Actual Value
The actual value of a CSS property is the used value of that property after any necessary approximations have been applied
The user agent performs four steps to calculate a property's actual (final) value:
- the specified value is determined based on the result of cascading, inheritance, or using the initial value.
- the computed value is calculated according to the specification (for example, a span with position: absolute will have its computed display changed to block)
- layout is calculated, resulting in the used value
- the used value is transformed according to the limitations of the local environment, resulting in the actual value
- initial.
- specified.
- computed.
- used.
- actual value.
CSS Logical Properties and Values
CSS Logical Basis
In position
/size
/margin
/padding
/border
/text alignment
:
block-start
fortop
.block-end
forbottom
.block
for vertical.inline-start
forleft
.inline-end
forright
.inline
for horizontal.
.logical {
/* stylelint-disable shorthand-property-no-redundant-values */
inset-block: 0 0;
inset-inline: 0 0;
inline-size: fit-content;
min-inline-size: min-content;
max-inline-size: max-content;
block-size: fit-content;
min-block-size: min-content;
max-block-size: max-content;
padding-block: 1rem 1rem;
padding-inline: 1rem 1rem;
margin-block: 1rem 1rem;
margin-inline: 1rem 1rem;
border-block-start: 1px solid blue;
border-block-end: 1px solid blue;
border-inline-start: 1px solid blue;
border-inline-end: 1px solid blue;
/* stylelint-enable shorthand-property-no-redundant-values */
}
CSS Logical Reference
CSS Variables
Scope Variables
Inherited Variables
CSS Variables 本质上具有继承特性, HTML 文档树中, 后代元素可以继承祖先元素的 CSS Variables:
<div class="alert alert-info">
<div class="alert-content">
<h2 class="alert-title">Info</h2>
<div class="alert-body">
<p>Info Message.</p>
</div>
</div>
</div>
Contextual Styling Variables
[data-theme='dark'] {
--fg: hsl(0deg 10% 70%);
--border: hsl(0deg 10% 10%);
--bg: hsl(0deg 0% 20%);
--button-bg: hsl(0deg 0% 25%);
--input-bg: hsl(0deg 0% 15%);
}
[data-theme='hero'] {
--fg: hsl(240deg 50% 90%);
--border: hsl(240deg 50% 10%);
--bg: hsl(240deg 33% 30%);
--button-bg: hsl(240deg 33% 40%);
--input-bg: hsl(240deg 33% 20%);
}
Contextual styling buttons:
:root {
--primary: hsl(260deg 95% 70%);
--secondary: hsl(320deg 95% 60%);
}
.button {
background-color: var(--button-background, transparent);
}
.button-primary {
--button-background: var(--primary);
}
.button-secondary {
--button-background: var(--secondary);
}
Contextual styling alerts:
.alert {
--primary: #777;
--secondary: #ccc;
background-color: var(--secondary);
border: 1px solid var(--primary);
}
.alert::before {
background-color: var(--primary);
}
.alert-title {
color: var(--primary);
}
.alert-success {
--primary: #40c057;
--secondary: #d3f9d8;
}
.alert-info {
--primary: #228be6;
--secondary: #d0ebff;
}
.alert-warning {
--primary: #fab005;
--secondary: #fff3bf;
}
.alert-error {
--primary: #fa5252;
--secondary: #ffe3e3;
}
Invalid and Empty Variables
--invalid-value: initial;
isinvalid
value leading tovar(--invalid-value)
called failed,var(--invalid-value, backup-value)
getbackup-value
.--empty-value: ;
is validempty
value leading tovar(--empty-value)
called succeeded,var(--empty-value, backup-value)
getunset
value (inherit
orinitial
value).- Use
invalid
andempty
value to implementif (true)
statement, you can see real world case ontailwind.css
.
:root {
--on: initial;
--off: ;
}
button {
--is-raised: var(--off);
border: 1px solid var(--is-raised, rgb(0 0 0 / 10%));
}
button:hover,
button:focus {
--is-raised: var(--on);
}
/**
* css-media-vars
* BSD 2-Clause License
* Copyright (c) James0x57, PropJockey, 2020
*/
html {
--media-print: initial;
--media-screen: initial;
--media-speech: initial;
--media-xs: initial;
--media-sm: initial;
--media-md: initial;
--media-lg: initial;
--media-xl: initial;
/* ... */
--media-pointer-fine: initial;
--media-pointer-none: initial;
}
/* 把当前变量变为空值 */
@media print {
html {
--media-print: ;
}
}
@media screen {
html {
--media-screen: ;
}
}
@media speech {
html {
--media-speech: ;
}
}
/* 把当前变量变为空值 */
@media (width <= 37.499em) {
html {
--media-xs: ;
--media-lte-sm: ;
--media-lte-md: ;
--media-lte-lg: ;
}
}
/** 移动优先的样式规则 */
.breakpoints-demo > * {
/** 小于 37.5em, 宽度 100% */
--xs-width: var(--media-xs) 100%;
/** 小于 56.249em, 宽度 49% */
--sm-width: var(--media-sm) 49%;
--md-width: var(--media-md) 32%;
--lg-width: var(--media-gte-lg) 24%;
width: var(--xs-width, var(--sm-width, var(--md-width, var(--lg-width))));
--sm-and-down-bg: var(--media-lte-sm) red;
--md-and-up-bg: var(--media-gte-md) green;
background: var(--sm-and-down-bg, var(--md-and-up-bg));
}
Space toggle for progressive enhancement
:root {
--in-oklab: ;
}
@supports (background: linear-gradient(in oklab, red, tan)) {
:root {
--in-oklab: in oklab;
}
}
/* Usage: */
.card {
background: linear-gradient(var(--in-oklab) #f00, #0f0);
}
Limit Variables
For some CSS values and units have limits (e.g <color>
),
use variables to implement if else
statement.
:root {
--red: 44;
--green: 135;
--blue: 255;
/**
* 亮度算法:
* lightness = (red * 0.2126 + green * 0.7152 + blue * 0.0722) / 255
*/
--lightness: calc((var(--red) * 0.2126 + var(--green) * 0.7152 + var(--blue) * 0.0722) / 255);
}
.button {
/* 文字颜色, 只可能是黑色或白色 */
color: hsl(0% 0% calc((var(--lightness) - 0.5) * -999999%));
/* 文字阴影, 黑色文字才会出现 */
text-shadow: 1px 1px
rgb(calc(var(--red) + 50) calc(var(--green) + 50) calc(var(--blue) + 50) / calc((var(--lightness) - 0.5) * 9999));
/* 背景颜色 */
background: rgb(var(--red) var(--green) var(--blue));
/* 固定样式 */
border: 0.2em solid;
/* 边框样式, 亮度大于 0.8 才出现 */
border-color: rgb(
calc(var(--red) - 50) calc(var(--green) - 50) calc(var(--blue) - 50) / calc((var(--lightness) - 0.8) * 100)
);
}
Dark Mode Variables
:root {
/* Themes */
--bg-light: #fff;
--text-light: #000;
--bg-dark: #000;
--text-dark: #fff;
/* Defaults */
--bg: var(--bg-light);
--text: var(--text-light);
}
@media (prefers-color-scheme: dark) {
:root {
--bg: var(--bg-dark);
--text: var(--text-dark);
}
}
Variables API
.element {
height: 100vh; /* Fallback for browsers that do not support Custom Properties */
height: calc(var(--vh, 1vh) * 100);
}
window.addEventListener('resize', () => {
const vh = window.innerHeight * 0.01
document.documentElement.style.setProperty('--vh', `${vh}px`)
})
const root = document.documentElement
const bgColor = getComputedStyle(root).getPropertyValue('--body-bg')
Change --cursor-x
and --cursor-y
via JavaScript
API:
:root::before {
position: fixed;
z-index: 1000;
display: block;
width: 100%;
height: 100%;
pointer-events: none;
content: '';
background: radial-gradient(
circle 16vmax at var(--cursor-x) var(--cursor-y),
rgb(0 0 0 / 0%) 0%,
rgb(0 0 0 / 50%) 80%,
rgb(0 0 0 / 80%) 100%
);
}
Change --percent
via JavaScript
API:
.bar {
display: flex;
height: 20px;
background-color: #f5f5f5;
}
.bar::before {
display: flex;
justify-content: end;
width: calc(var(--percent) * 1%);
font-size: 12px;
color: #fff;
white-space: nowrap;
content: counter(progress) '%\2002';
counter-reset: progress var(--percent);
background: #2486ff;
}
Properties and Values API
@property --property-name {
syntax: '<color>';
inherits: false;
initial-value: #c0ffee;
}
window.CSS.registerProperty({
name: '--my-color',
syntax: '<color>',
inherits: false,
initialValue: '#c0ffee',
})
CSS 不支持背景渐变色的直接过渡动画,
需要使用两层背景渐变 (background
+ ::before
/::after
background
)
opacity
变化
实现渐变背景的过渡动画.
现在,
可以对 CSS Houdini 自定义变量
设置 transition
/animation
,
快速实现渐变背景的过渡动画:
@property --houdini-color-a {
syntax: '<color>';
inherits: false;
initial-value: #fff;
}
@property --houdini-color-b {
syntax: '<color>';
inherits: false;
initial-value: fuchsia;
}
.box {
background: linear-gradient(45deg, var(--houdini-color-a), var(--houdini-color-b));
/* stylelint-disable-next-line custom-property-no-missing-var-function */
transition: 1s --houdini-color-a;
animation: change 10s infinite linear;
}
.box:hover {
--houdini-color-a: yellowgreen;
}
@keyframes change {
20% {
--houdini-color-b: red;
}
40% {
--houdini-color-b: #ff3c41;
}
60% {
--houdini-color-b: orange;
}
80% {
--houdini-color-b: #ae63e4;
}
}
@property --per {
syntax: '<percentage>';
inherits: false;
initial-value: 25%;
}
.pie {
background: conic-gradient(yellowgreen, yellowgreen var(--per), transparent var(--per), transparent 100%);
/* stylelint-disable-next-line custom-property-no-missing-var-function */
transition: --per 300ms linear;
}
.pie:hover {
--per: 60%;
}
@property
feature detection and fallback:
@property --parent-em {
syntax: '<length>';
initial-value: 0;
inherits: true;
}
@property --no-at-property-fallback {
syntax: '*';
inherits: false;
}
select {
--parent-em: 1em;
--no-at-property-fallback: 1em;
}
optgroup {
/* Will only inherit if `@property` not supported */
font-size: var(--no-at-property-fallback, 0);
}
optgroup > * {
font-size: var(--parent-em);
}
CSS Colors
Current Color
currentcolor
变量使用当前color
计算值.border-color
/outline-color
/caret-color
/text-shadow
/box-shadow
默认表现为currentcolor
.
Accent Color
Change user-interface controls accent color.
HSL Color
- H: hue.
- S: saturation (stay
50%
etc.). - L: lightness (easy to theme colors).
Change hue
to get color palette:
- Complement
hue
:180deg
,brand
andsecondary
color. - Mix
hue
to get natural color.
Change lightness
to get color palette:
- Decease
lightness
to get:hover
/:focus
color. - Increase
lightness
to getsecondary
/ghost
color.
/* Hover Button */
:root {
--primary-h: 221;
--primary-s: 72%;
--primary-l: 62%;
}
.button {
background-color: hsl(var(--primary-h) var(--primary-s) var(--primary-l));
}
.button:hover,
.button:focus {
--primary-l: 54%;
}
.button-secondary {
--primary-l: 90%;
color: #222;
}
.button-ghost {
--primary-l: 90%;
background-color: transparent;
border: 3px solid hsl(var(--primary-h) var(--primary-s) var(--primary-l));
}
Change lightness
to get gradient color:
.section {
background: linear-gradient(
to left,
hsl(var(--primary-h) var(--primary-s) var(--primary-l)),
hsl(var(--primary-h) var(--primary-s) 95%)
);
}
.section-2 {
--primary-h: 167;
}
HWB Color
H
: hue (<angle>
).W
: whiteness (<percentage>
).B
: blackness (<percentage>
).A
: alpha (<percentage>
).
Color Scheme
:root {
color-scheme: normal;
color-scheme: light dark;
color-scheme: light;
color-scheme: dark;
}
CSS Color Mix
Creating color palettes with color-mix()
:
:root {
--yellow: rgb(221 215 141);
--peach: rgb(220 191 133);
--chocolate: rgb(139 99 92);
--khaki: rgb(96 89 77);
--grey: rgb(147 162 155);
--mix-warm: red;
--mix-cool: blue;
}
.palette > div {
--color: var(--yellow);
background: color-mix(in srgb, var(--color), var(--mix, var(--color)) var(--amount, 10%));
&:nth-child(2) {
--color: var(--peach);
}
&:nth-child(3) {
--color: var(--chocolate);
}
&:nth-child(4) {
--color: var(--khaki);
}
&:nth-child(5) {
--color: var(--grey);
}
}
.cool {
--mix: var(--mix-cool);
}
.cool-20 {
--amount: 20%;
}
.warm {
--mix: var(--mix-warm);
}
.warm-20 {
--amount: 20%;
}
CSS Color Reference
- CSS color module level 5 guide:
- hwb.
- lab.
- lch.
- color-mix.
- color-contrast.
- color.
- accent-color.
- CSS
color
value.
CSS Math
Calculation Function
- 支持多种数据类型:
<length>
/<frequency>
/<angle>
/<time>
/<percentage>
/<number>
/<integer>
. - 支持加减乘除 4 种运算.
- 运算符前后带单位或者带百分号的值只能进行加减运算, 不能进行乘除运算.
- 加号和减号两侧一定要有空格, 乘号和除号两侧无须空格.
- 结合
CSS Variables
, 拥有强大功能与可维护性.
html {
font-size: calc(16px + 2 * (100vw - 375px) / 39);
}
.button {
width: calc(100% - 20px);
}
.list {
--size: calc(100% - 2rem);
width: calc(var(--size) / 6);
}
If calc()
result breaks,
check cache plugin or build tool.
Some tools like to remove whitespace always
lead to broken calc()
addition and subtraction operator.
Min and Max Function
.box {
width: min(100vw - 3rem, 80ch);
width: max(10px * 10, 10em);
width: min(calc(10px * 10), 10em);
width: max(10px * 10, var(--width));
margin-block-start: min(4rem, 8vh);
}
.legacy-container {
width: 100%;
max-width: 1024px;
}
.modern-container {
width: min(100%, 1024px);
}
.legacy-container {
width: 100%;
min-width: 768px;
}
.modern-container {
width: max(100%, 768px);
}
Clamp Function
.clamp {
width: max(75px, min(25vw, 125px));
width: clamp(30ch, 80%, 80ch);
padding: clamp(1rem, 3%, 1.5rem);
margin-bottom: clamp(4px, 6.5vh, 5.5rem);
font-size: clamp(2.25rem, 2vw + 1.5rem, 3.25rem);
text-indent: clamp(15px, 10%, 1.5rem);
letter-spacing: clamp(0.1rem, 1.5vw, 0.5rem);
}
Generate fluid size for Tailwind.css
:
const settings = {
typography: {
fontSizeMin: 1.125,
fontSizeMax: 1.25,
msFactorMin: 1.125,
msFactorMax: 1.2,
lineHeight: 1.6,
},
screensRem: {
'min': 20,
'sm': 40,
'md': 48,
'lg': 64,
'xl': 80,
'2xl': 96,
},
grid: {
cols: 24,
},
}
const remToPx = rem => `${rem * 16}px`
const screens = {
'sm': remToPx(settings.screensRem.sm),
'md': remToPx(settings.screensRem.md),
'lg': remToPx(settings.screensRem.lg),
'xl': remToPx(settings.screensRem.xl),
'2xl': remToPx(settings.screensRem['2xl']),
}
const fsMin = settings.typography.fontSizeMin
const fsMax = settings.typography.fontSizeMax
const msFactorMin = settings.typography.msFactorMin
const msFactorMax = settings.typography.msFactorMax
const screenMin = settings.screensRem.min
const screenMax = settings.screensRem['2xl']
function calcMulti(multiMin = 0, multiMax = null) {
return {
fsMin: fsMin * msFactorMin ** multiMin,
fsMax: fsMax * msFactorMax ** (multiMax || multiMin),
}
}
function clamp(multiMin = 0, multiMax = null) {
const _calcMulti = calcMulti(multiMin, multiMax || multiMin)
const _fsMin = _calcMulti.fsMin
const _fsMax = _calcMulti.fsMax
return `clamp(${_fsMin}rem, calc(${_fsMin}rem + (${_fsMax} - ${_fsMin}) * ((100vw - ${screenMin}rem) / (${screenMax} - ${screenMin}))), ${_fsMax}rem)`
}
const fontSize = {
'xs': clamp(-2),
'sm': clamp(-1),
'base': clamp(0),
'lg': clamp(1),
'xl': clamp(2),
'2xl': clamp(3),
'3xl': clamp(4),
'4xl': clamp(5),
'5xl': clamp(6),
'6xl': clamp(7),
'7xl': clamp(8),
'8xl': clamp(9),
'9xl': clamp(10),
}
module.exports = {
theme: {
screens,
fontSize,
},
}
:root {
/* Fluid type scale */
--size-step-minus-2: clamp(0.6944rem, 0.6376rem + 0.284vi, 0.84rem);
--size-step-minus-1: clamp(0.8333rem, 0.7488rem + 0.4228vi, 1.05rem);
--size-step-0: clamp(1rem, 0.878rem + 0.6098vi, 1.3125rem);
--size-step-1: clamp(1.2rem, 1.028rem + 0.8598vi, 1.6406rem);
--size-step-2: clamp(1.44rem, 1.2016rem + 1.1918vi, 2.0508rem);
--size-step-3: clamp(1.728rem, 1.402rem + 1.6302vi, 2.5635rem);
--size-step-4: clamp(2.0736rem, 1.6323rem + 2.2063vi, 3.2043rem);
--size-step-5: clamp(2.4883rem, 1.8963rem + 2.9602vi, 4.0054rem);
--size-step-6: clamp(2.986rem, 2.1974rem + 3.943vi, 5.0068rem);
--size-step-7: clamp(3.5832rem, 2.5392rem + 5.2201vi, 6.2585rem);
/* Fluid space scale */
--space-3xs: clamp(0.25rem, 0.2256rem + 0.122vi, 0.3125rem);
--space-2xs: clamp(0.5rem, 0.4268rem + 0.3659vi, 0.6875rem);
--space-xs: clamp(0.75rem, 0.6524rem + 0.4878vi, 1rem);
--space-s: clamp(1rem, 0.878rem + 0.6098vi, 1.3125rem);
--space-m: clamp(1.5rem, 1.3049rem + 0.9756vi, 2rem);
--space-l: clamp(2rem, 1.7561rem + 1.2195vi, 2.625rem);
--space-xl: clamp(3rem, 2.6341rem + 1.8293vi, 3.9375rem);
--space-2xl: clamp(4rem, 3.5122rem + 2.439vi, 5.25rem);
--space-3xl: clamp(6rem, 5.2683rem + 3.6585vi, 7.875rem);
}
Create fluid typography with clamp()
:
- Calculating factor:
factor =
(max-value - min-value ) / (max-viewport-width - min-viewport-width)
. - Calculating relative value:
relative-value =
min-value - min-viewport-width * factor
. - Calculating preferred value:
fluid-value (
vw
) =100vw * factor
.
.fluid-typography {
--fluid-value: clamp(min-value, relative-value + fluid-value, max-value);
}
CSS Text
Text Alignment
- 对 block level element 无效.
start
/end
/left
/right
/center
/match-parent
.justify
: 自适应, 左右都无空格.
.wrap {
text-align: justify;
text-align-last: justify; /* 一个块或行的最后一行对齐方式 */
text-justify: distribute-all-lines; /* ie6-8 */
}
最后一行文字对齐方式.
- type of justification for
text-align: justify
. none
: turn offtext-align: justify
.auto
.inter-word
.inter-character
.
Text Indent
- 作用于 block container, 但实际作用于第一行内联盒子内容.
- 对
display: inline
替换元素无效. - 对
display: inline-*
替换元素有效. - Percentage
text-indent
calculate bycontaining block
width.
.hidden-text {
font: 0/0;
color: transparent;
text-indent: -9999px;
}
pre {
font-size: 100%;
tab-size: 2;
white-space: pre-wrap;
}
Text Spacing
在设计领域, 文本行之间的距离称为行距 (leading
),
来源于印刷版每行文字之间添加的一条条的引导线 (lead
).
字符之间的距离称之为字距 (tracking
).
tailwind.css
使用 leading-{size}
控制 line-height
,
使用 tracking-{size}
控制 letter-spacing
.
letter-spacing
:
- 继承性.
- 默认值为
normal
. - 支持负值, 小数值.
word-spacing
:
- 继承性.
- 默认值为
normal
. - 支持负值, 小数值, 百分比.
- 最终间隔距离会受
text-align: justify
影响.
.paragraph {
line-height: 1.5em; /* 行间距 */
text-indent: 2em; /* 段落缩进 */
letter-spacing: 50px; /* 字间距 */
word-spacing: 50px; /* 词间距 */
}
Text Transform
p {
font-variant: small-caps; /* 小型的大写字母 */
text-transform: uppercase; /* 大写字母 */
text-transform: lowercase; /* 小写字母 */
text-transform: capitalize; /* 首字母大写 */
}
Text Decoration
.formal-syntax {
text-decoration: < 'text-decoration-line' > || < 'text-decoration-style' > || < 'text-decoration-color' > || <
'text-decoration-thickness' >;
}
.line {
text-decoration-line: overline; /* 上划线 */
text-decoration-line: line-through; /* 中划线 */
text-decoration-line: underline; /* 下划线 */
}
.text {
text-decoration: underline;
text-decoration: dotted underline;
text-decoration: red underline dashed;
text-decoration: wavy underline 3px red;
}
.wavy {
display: block;
height: 0.5rem;
padding-top: 0.5rem;
overflow: hidden;
letter-spacing: 100vw;
white-space: nowrap;
}
.wavy::before {
text-decoration: overline; /* IE */
text-decoration-style: wavy;
content: '\2000\2000';
}
下划线样式:
Text Emphasis
<'text-emphasis-style'> || <'text-emphasis-color'>
.text-emphasis-style
:none
.<character>
.[ filled | open ] || [ dot | circle | double-circle | triangle | sesame ]
.
text-emphasis-color
:currentcolor | <color>
.- 重点符号字号默认为文字字号的一半.
.text {
/* Initial value */
text-emphasis: none;
/* <string> value */
text-emphasis: 'x';
text-emphasis: '点';
text-emphasis: '\25B2';
text-emphasis: '*' #555;
/* Keywords value */
text-emphasis: filled; /* filled dot */
text-emphasis: open; /* open dot */
text-emphasis: sesame; /* filled sesame */
text-emphasis: open sesame;
/* Keywords value combined with a color */
text-emphasis: filled sesame #555;
}
[ over | under ] && [ right | left ]
.over
: draws marks over text in horizontal writing mode.under
: draws marks under text in horizontal writing mode.right
: draws marks to right of text in vertical writing mode.left
: draws marks to left of text in vertical writing mode.- 默认在顶部或右侧画重点符号.
.text {
/* Initial value */
text-emphasis-position: over right;
/* Keywords value */
text-emphasis-position: over left;
text-emphasis-position: under right;
text-emphasis-position: left under;
text-emphasis-position: right over;
}
Text Size Adjust
禁止 iOS 横屏字号自动调整:
body {
/* stylelint-disable-next-line property-no-vendor-prefix */
-webkit-text-size-adjust: none;
}
Text Overflow
clip
: 切除溢出部分.ellipsis
: 省略号标志 (要设置width
).
.truncation-article-container {
width: 500px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.article-container {
display: box;
overflow: hidden;
text-overflow: ellipsis;
-webkit-line-clamp: 4; /* 需要显示的行数 */
word-break: break-all;
-webkit-box-orient: vertical;
}
White Space
Web default:
- 空格被解析为换行.
- 换行被解析为空格.
- 自动合并空格.
普通标签内自动忽略空格符,
并将其与空白符转换成一个空格进行输出,
可用 white-space
改变这一行为:
White Space | 换行符 | 空格和制表符 | 文字换行 | 行尾空格 |
---|---|---|---|---|
normal | 合并 | 合并 | 换行 | 删除 |
nowrap | 合并 | 合并 | 不换行 | 删除 |
pre | 保留 | 保留 | 不换行 | 保留 |
pre-wrap | 保留 | 保留 | 换行 | 挂起 |
pre-line | 保留 | 合并 | 换行 | 删除 |
break-spaces | 保留 | 保留 | 换行 | 换行 |
dd + dt::before {
white-space: pre;
content: '\A';
}
dd + dd::before {
margin-left: -0.25em;
font-weight: normal;
content: ', ';
}
Text Wrap
Text wrapping and word breaking:
word-break
:normal
: default line break rule.keep-all
: Word breaks should not be used for CJK text. Non-CJK text behavior is same as fornormal
.break-all
: word breaks should be inserted between any two characters (excluding CJK text).
overflow-wrap
(word-wrap
):normal
.anywhere
.break-word
.
line-break
(break lines of CJK text when working with punctuation and symbols):auto
.loose
.normal
.strict
.anywhere
.
hyphens
(how words should be hyphenated when text wraps across multiple lines):manual
: words are broken for line-wrapping only where-
or­
.auto
: automatically break words at appropriate hyphenation points.none
: words are not broken at line breaks.
<wbr>
: word break opportunity.
/* 不换行 */
.nowrap {
white-space: nowrap;
}
/* 自动换行 */
.auto-wrap {
hyphens: auto;
word-break: normal;
word-wrap: break-word;
line-break: anywhere;
}
/* 自动换行 */
pre {
hyphens: auto;
word-wrap: break-word; /* IE 5.5-7 */
white-space: pre-wrap; /* Modern Browsers */
line-break: anywhere;
}
/* 强制换行 */
.force-wrap {
word-break: break-all;
line-break: anywhere;
}
/* IE not support <wbr> */
wbr::after {
content: '\00200B';
}
- 避头标点: 不在行首显示的标点, e.g 逗号, 顿号, 句号, 问号, 叹号.
- 避尾标点: 不在行尾显示的标点, e.g 前引号, 前括号.
.text-truncate-box {
display: inline-block;
max-width: 250px;
overflow: hidden;
text-overflow: ellipsis;
word-wrap: normal;
white-space: nowrap;
}