Skip to main content

Interaction

Cursor

cursor:

  • auto/default/pointer/move/text.
  • wait/help/crosshair/progress.
  • not-allowed/no-drop/vertical-text/all-scroll.
  • none/alias/cell/copy/context-menu.
  • grab/grabbing.
  • n-resize/e-resize/s-resize/w-resize.
  • ne-resize/nw-resize/se-resize/sw-resize.
  • col-resize/row-resize.
  • ew-resize/ns-resize/nesw-resize/nwse-resize.
  • zoom-in/zoom-out.
  • url().
.wrap {
pointer-events: none;
cursor: default;
}

Pointer Events

pointer-events:

  • auto.
  • none:
    • 不能阻止键盘行为: 元素依然可以通过 Tab 键被 focus.
    • 无法显示 [title]A11Y 提示, 影响无障碍访问 (特别是移动端).
    • 综上所述, none 不适合 <a>/<button> 等控件元素, 适合作用在装饰性的或仅用作视觉表现的非控件元素.
  • 具有继承性.

Touch Action

touch-action:

  • auto: enable all panning and zooming gestures.
  • none: disable all panning and zooming gestures.
  • manipulation: only enable panning and pinch zoom gestures (滚动, 持续缩放), remove click delay (300ms) for mobile device.
  • [pan-x | pan-left | pan-right ] || [ pan-y | pan-up | pan-down ] || pinch-zoom.
.box {
touch-action: auto;
touch-action: none;
touch-action: manipulation;
touch-action: pan-x;
touch-action: pan-left;
touch-action: pan-right;
touch-action: pan-y;
touch-action: pan-up;
touch-action: pan-down;
touch-action: pinch-zoom;
touch-action: pan-left pan-up;
}

Resize

resize:

  • none.
  • both.
  • horizontal.
  • vertical.
  • inline.
  • block.
  • 不支持内联元素.
  • 支持 overflow non-visible 块级元素.
  • 可用 min-width/min-height/max-width/max-height 限制拉伸范围.
.editable {
overflow: hidden;
resize: both;
}

Image comparison slider:

<div class="image-slider">
<div class="image-before">
<img src="cat-before.jpg" alt="Before" />
</div>
<img class="image-after" src="cat-after.jpg" alt="After" />
<input type="range" class="image-a11y-control" />
</div>

<style>
.image-slider {
position: relative;
display: inline-block;
}

.image-slider img {
display: block;
user-select: none;
}

.image-slider > div {
position: absolute;
top: 0;
bottom: 0;
left: 0;
width: 50%; /* resizable width */
max-width: 100%;
overflow: hidden;
resize: horizontal;
}

.image-slider > div::before {
position: absolute;
right: 0;
bottom: 0;
width: 12px;
height: 12px;
padding: 5px;
cursor: ew-resize;
content: '';
background: linear-gradient(-45deg, white 50%, transparent 0);
background-clip: content-box;
}
</style>

<script>
/* Additional range input feature */
const range = document.querySelector('.image-a11y-control')
range.addEventListener('input', (event) => {
document.querySelector('.image-before').style.width = `${event.target.value}%`
})
</script>

User Select

禁止图文被选中, 保持和原生 App 一样的文字选中体验:

.body {
-webkit-touch-callout: none;
user-select: none;
}

点击任意位置实现全选效果:

.box {
user-select: all;
}

Caret Color

输入框光标颜色:

input {
caret-color: red;
}

Scroll Behavior

scroll-behavior:

  • auto.
  • smooth.
html,
body {
scroll-behavior: smooth;
}

Overscroll Behavior

overscroll-behavior:

  • auto{1,2}.
  • contain{1,2}: 嵌套滚动不会传导至相邻区域, 即嵌套滚动条滚动到底部便停止, 不会继续滚动外部滚动条.
  • none{1,2}.

Scroll Snap

scroll-snap:

<div class="scroll-x">
<img src="1.jpg" />
<img src="2.jpg" />
<img src="3.jpg" />
<img src="4.jpg" />
</div>

<style>
.scroll-x {
max-width: 414px;
height: 420px;
overflow: auto;
white-space: nowrap;
scroll-snap-type: x mandatory;
}

.scroll-x img {
scroll-snap-align: center;
scroll-snap-stop: always;
}
</style>

Scroll Snap Type

scroll-snap-type:

  • none.
  • [ x | y | inline | block | both ] [ mandatory | proximity ]?:
    • x: 捕捉水平定位点.
    • y: 捕捉垂直平定位点.
    • inline: 捕捉和内联元素排列一个滚动方向的定位点, 默认文档流下指的就是水平轴.
    • block: 捕捉和块状元素排列一个滚动方向的定位点, 默认文档流下指的就是垂直轴.
    • both: 横轴与纵轴都捕捉.
    • mandatory: 强制定位, 若存在有效的定位点位置, 则滚动容器必须在滚动结束时进行定位.
    • proximity: 大约定位, 让浏览器自己判断要不要定位.

Scroll Padding

scroll-padding:

  • auto{1,4}.
  • <length-percentage>{1,4}.

Scroll Snap Align

scroll-snap-align:

  • none{1,2}.
  • start{1,2}: 起始位置对齐, 子元素对齐容器左边缘/上边缘.
  • end{1,2}: 结束位置对齐, 子元素对齐容器右边缘/下边缘.
  • center{1,2}: 居中对齐, 子元素中心和滚动容器中心一致.

Scroll Snap Stop

scroll-snap-stop:

  • normal: 可以忽略捕获位置.
  • always: 不能忽略捕获位置, 且必须定位到第一个捕获元素的位置, 保证每次只滚动一屏或一个指定元.

Scroll Margin

scroll-margin:

  • 0.
  • <length>{1,4}.

Overflow Anchor

overflow-anchor:

  • auto: 开启滚动锚定.
  • none: 关闭滚动锚定.

Overflow Scrolling

overflow: scroll 剪裁界线为 padding box. 但部分浏览器不符合这一标准. 实际项目开发时, 要尽量避免滚动容器设置 padding-bottom 值:

  • 跨浏览器样式表现不一致.
  • 跨浏览器 scrollHeight 值不一致.

Horizontal Scrolling

Horizontal Scrolling Principles

Horizontal scrolling design:

  • Avoid a full-screen horizontal scroll; ensure that users know there is also content that can be reached using a traditional method.
  • Make scroll interactions obvious and provide instruction.
  • To avoid accessibility issues, ensure that horizontal scrolling elements also work with keyboard navigation.
  • Design horizontal scrolling elements in containers using HTML and CSS.
  • Use visual cues, such as partial images, to show that there is a horizontal scroll action in effect.
  • Use partial horizontal scrolling with a static design element for stability.
  • Design horizontal scroll bars in the same manner as vertical scroll bars to create an element of familiarity for users.

Horizontal Scrolling Methods

  • Transform: rotate 90deg element.
  • Flex.
  • Grid.
  • Scroll snap.
.scroll-container {
width: 100%;
overflow: auto hidden;
white-space: nowrap;

/* Hide scrollbar in IE and Firefox */
-ms-overflow-style: none;
scrollbar-width: none;
}

/* Hide scrollbar in Chrome */
.scroll-container::-webkit-scrollbar {
display: none;
}

.scroll-container .item {
display: inline-flex;
}

@supports (display: grid) {
.scroll-container {
display: grid;
grid-template-rows: auto;
grid-auto-flow: column;
max-width: min-content;
}
}

@supports (scroll-snap-type: x mandatory) {
.scroll-container {
scroll-snap-type: x mandatory;
scroll-padding: 0 1.2rem;
}

.scroll-container .item {
scroll-snap-align: center;
}
}

Custom Scrollbar

Scroll bars take up space within padding box.

Standard Custom Scrollbar

scrollbar-width:

  • auto.
  • thin.
  • none.

scrollbar-color:

  • auto.
  • <color>{2}.

Chrome Custom Scrollbar

WebKit scrollbar:

  • 整体部分: ::-webkit-scrollbar.
  • 两端按钮: ::-webkit-scrollbar-button.
  • 外层轨道: ::-webkit-scrollbar-track.
  • 内层轨道: ::-webkit-scrollbar-track-piece.
  • 滚动滑块: ::-webkit-scrollbar-thumb.
  • 边角: ::-webkit-scrollbar-corner.
.demo::-webkit-scrollbar {
/* 滚动条整体样式 */

/* 高宽分别对应横竖滚动条的尺寸 */
width: 10px;
height: 1px;
}

.demo::-webkit-scrollbar-thumb {
background-color: blue;
background-image: linear-gradient(
45deg,
rgb(255 255 255 / 20%) 25%,
transparent 25%,
transparent 50%,
rgb(255 255 255 / 20%) 50%,
rgb(255 255 255 / 20%) 75%,
transparent 75%,
transparent
);

/* 滚动条方块 */
border-radius: 10px;
}

.demo::-webkit-scrollbar-track {
background-color: #ededed;

/* 滚动条轨道 */
border-radius: 10px;
box-shadow: inset 0 0 5px rgb(0 0 0 / 20%);
}

Hidden Custom Scrollbar

.scroll-none {
overflow: hidden auto;
scrollbar-width: none; /* FireFox */
-ms-overflow-style: none; /* IE 10+ */
}

.scroll-none::-webkit-scrollbar {
/* Chrome Safari */
display: none;
width: 0;
height: 0;
}

Parallax Effect

Background Attachment Parallax Effect

.parallax {
min-height: 60%; /* key */
background-image: url('./images/bg.jpg');
background-repeat: no-repeat;
background-attachment: fixed; /* key */
background-position: center;
background-size: cover;
}

3D Perspective Transform Parallax Effect

3D perspective transform parallax effect:

<div class="parallax">
<div class="parallax-group">
<div class="parallax-layer parallax-fore-layer"></div>
<div class="parallax-layer parallax-base-layer"></div>
</div>
<div class="parallax-group">
<div class="parallax-layer parallax-base-layer"></div>
<div class="parallax-layer parallax-back-layer"></div>
<div class="parallax-layer parallax-deep-layer"></div>
</div>
</div>

<style>
.parallax {
height: 100vh;
overflow: hidden auto;
perspective: 1px;
perspective-origin: 100% 50%;
}

.parallax-group {
position: relative;
height: 100vh;
transform-style: preserve-3d;
}

.parallax-layer {
position: absolute;
inset: 0;
transform-origin: 100% 50%;
}

.parallax-fore-layer {
transform: translateZ(90px) scale(0.7);
}

.parallax-base-layer {
transform: translateZ(0);
}

.parallax-back-layer {
transform: translateZ(-1px) scale(2);
}

.parallax-deep-layer {
transform: translateZ(-2px) scale(3);
}
</style>

Page Progress Indicator

body {
position: relative;
}

.indicator {
position: absolute;
inset: 0;
z-index: 1;
pointer-events: none;
background: linear-gradient(to right top, teal 50%, transparent 50%) no-repeat;
background-size: 100% calc(100% - 100vh);
mix-blend-mode: darken;
}

/* use after element to hidden triangle background gradient */

/* only show 5px background */
.indicator::after {
position: fixed;
inset: 5px 0 0;
z-index: 1;
content: '';
background: #fff;
}