pseudo-elements by raphaelsalaja/userinterface-wiki
npx skills add https://github.com/raphaelsalaja/userinterface-wiki --skill pseudo-elements审查 CSS 和 JavaScript 中关于伪元素的最佳实践以及视图过渡 API 的使用情况。
文件:行号 格式输出检查结果| 优先级 | 类别 | 前缀 |
|---|---|---|
| 1 | Before/After | pseudo- |
| 2 | 视图过渡 | transition- |
| 3 | 原生样式 | native- |
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
pseudo-content-required::before 和 ::after 需要 content 属性才能渲染。
失败示例:
.button::before {
position: absolute;
background: var(--gray-3);
}
通过示例:
.button::before {
content: "";
position: absolute;
background: var(--gray-3);
}
pseudo-over-dom-node使用伪元素来添加装饰性内容,而不是额外的 DOM 节点。
失败示例:
<button className={styles.button}>
<span className={styles.background} /> {/* 不必要的 DOM 节点 */}
Click me
</button>
通过示例:
<button className={styles.button}>
Click me
</button>
.button::before {
content: "";
/* 装饰性背景 */
}
pseudo-position-relative-parent父元素必须具有 position: relative,以便绝对定位的伪元素正常工作。
失败示例:
.button::before {
content: "";
position: absolute;
inset: 0;
}
/* .button 没有 position 属性 */
通过示例:
.button {
position: relative;
}
.button::before {
content: "";
position: absolute;
inset: 0;
}
pseudo-z-index-layering伪元素需要 z-index 才能与内容正确分层。
失败示例:
.button::before {
content: "";
position: absolute;
inset: 0;
background: var(--gray-3);
}
/* 覆盖了按钮文字 */
通过示例:
.button {
position: relative;
z-index: 1;
}
.button::before {
content: "";
position: absolute;
inset: 0;
background: var(--gray-3);
z-index: -1;
}
pseudo-hit-target-expansion使用负的 inset 值来扩大点击目标区域,无需额外的标记。
失败示例:
<div className={styles.wrapper}> {/* 用于点击目标的额外包装器 */}
<a className={styles.link}>Link</a>
</div>
通过示例:
.link {
position: relative;
}
.link::before {
content: "";
position: absolute;
inset: -8px -12px;
}
transition-name-required参与视图过渡的元素需要设置 view-transition-name。
失败示例:
document.startViewTransition(() => {
// 未分配 view-transition-name
targetImg.src = newSrc;
});
通过示例:
sourceImg.style.viewTransitionName = "card";
document.startViewTransition(() => {
sourceImg.style.viewTransitionName = "";
targetImg.style.viewTransitionName = "card";
});
transition-name-unique在过渡期间,页面上的每个 view-transition-name 必须是唯一的。
失败示例:
.card {
view-transition-name: card;
}
/* 多个卡片使用了相同的名称 */
通过示例:
// 仅对正在过渡的元素分配唯一名称
element.style.viewTransitionName = `card-${id}`;
transition-name-cleanup在过渡完成后移除 view-transition-name。
失败示例:
sourceImg.style.viewTransitionName = "card";
document.startViewTransition(() => {
targetImg.style.viewTransitionName = "card";
});
// sourceImg 仍然保留名称,会导致下一次过渡冲突
通过示例:
sourceImg.style.viewTransitionName = "card";
document.startViewTransition(() => {
sourceImg.style.viewTransitionName = "";
targetImg.style.viewTransitionName = "card";
});
transition-over-js-library对于页面过渡,优先使用视图过渡 API 而非 JavaScript 动画库。
失败示例:
import { motion } from "motion/react";
function ImageLightbox() {
return (
<motion.img layoutId="hero" /> // 基于 JS 的共享元素过渡
);
}
通过示例:
function openLightbox(img: HTMLImageElement) {
img.style.viewTransitionName = "hero";
document.startViewTransition(() => {
// 原生浏览器过渡
});
}
transition-style-pseudo-elements为自定义动画设置视图过渡伪元素的样式。
失败示例:
document.startViewTransition(() => { /* ... */ });
// 使用默认的交叉淡入淡出效果
通过示例:
::view-transition-group(card) {
animation-duration: 300ms;
animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
}
native-backdrop-styling使用 ::backdrop 伪元素来设置对话框/弹出框的背景。
失败示例:
<>
<div className={styles.overlay} onClick={close} />
<dialog className={styles.dialog}>{children}</dialog>
</>
通过示例:
dialog::backdrop {
background: var(--black-a6);
backdrop-filter: blur(4px);
}
native-placeholder-styling使用 ::placeholder 来设置输入框占位符的样式,而不是使用包装元素。
失败示例:
<div className={styles.inputWrapper}>
{!value && <span className={styles.placeholder}>Enter text...</span>}
<input value={value} />
</div>
通过示例:
input::placeholder {
color: var(--gray-9);
opacity: 1;
}
native-selection-styling使用 ::selection 来设置文本选中区域的样式。
通过示例:
::selection {
background: var(--blue-a5);
color: var(--gray-12);
}
审查文件时,按以下格式输出结果:
文件:行号 - [规则ID] 问题描述
示例:
components/button/styles.module.css:12 - [pseudo-content-required] ::before 缺少 content 属性
components/lightbox/index.tsx:45 - [transition-over-js-library] 使用 motion layoutId 而不是视图过渡 API
在检查结果之后,输出一个汇总:
| 规则 | 数量 | 严重程度 |
|---|---|---|
pseudo-content-required | 2 | 高 |
pseudo-over-dom-node | 1 | 中 |
transition-name-cleanup | 1 | 中 |
每周安装量
139
仓库
GitHub 星标数
641
首次出现
2026年1月26日
安全审计
安装于
opencode119
cursor115
codex112
claude-code107
gemini-cli106
github-copilot95
Review CSS and JavaScript for pseudo-element best practices and View Transitions API usage.
file:line format| Priority | Category | Prefix |
|---|---|---|
| 1 | Before/After | pseudo- |
| 2 | View Transitions | transition- |
| 3 | Native Styling | native- |
pseudo-content-required::before and ::after require content property to render.
Fail:
.button::before {
position: absolute;
background: var(--gray-3);
}
Pass:
.button::before {
content: "";
position: absolute;
background: var(--gray-3);
}
pseudo-over-dom-nodeUse pseudo-elements for decorative content instead of extra DOM nodes.
Fail:
<button className={styles.button}>
<span className={styles.background} /> {/* Unnecessary DOM node */}
Click me
</button>
Pass:
<button className={styles.button}>
Click me
</button>
.button::before {
content: "";
/* decorative background */
}
pseudo-position-relative-parentParent must have position: relative for absolute pseudo-elements.
Fail:
.button::before {
content: "";
position: absolute;
inset: 0;
}
/* .button has no position */
Pass:
.button {
position: relative;
}
.button::before {
content: "";
position: absolute;
inset: 0;
}
pseudo-z-index-layeringPseudo-elements need z-index to layer correctly with content.
Fail:
.button::before {
content: "";
position: absolute;
inset: 0;
background: var(--gray-3);
}
/* Covers button text */
Pass:
.button {
position: relative;
z-index: 1;
}
.button::before {
content: "";
position: absolute;
inset: 0;
background: var(--gray-3);
z-index: -1;
}
pseudo-hit-target-expansionUse negative inset values to expand hit targets without extra markup.
Fail:
<div className={styles.wrapper}> {/* Extra wrapper for hit target */}
<a className={styles.link}>Link</a>
</div>
Pass:
.link {
position: relative;
}
.link::before {
content: "";
position: absolute;
inset: -8px -12px;
}
transition-name-requiredElements participating in view transitions need view-transition-name.
Fail:
document.startViewTransition(() => {
// No view-transition-name assigned
targetImg.src = newSrc;
});
Pass:
sourceImg.style.viewTransitionName = "card";
document.startViewTransition(() => {
sourceImg.style.viewTransitionName = "";
targetImg.style.viewTransitionName = "card";
});
transition-name-uniqueEach view-transition-name must be unique on the page during transition.
Fail:
.card {
view-transition-name: card;
}
/* Multiple cards with same name */
Pass:
// Assign unique name only to transitioning element
element.style.viewTransitionName = `card-${id}`;
transition-name-cleanupRemove view-transition-name after transition completes.
Fail:
sourceImg.style.viewTransitionName = "card";
document.startViewTransition(() => {
targetImg.style.viewTransitionName = "card";
});
// sourceImg still has name, causes conflict on next transition
Pass:
sourceImg.style.viewTransitionName = "card";
document.startViewTransition(() => {
sourceImg.style.viewTransitionName = "";
targetImg.style.viewTransitionName = "card";
});
transition-over-js-libraryPrefer View Transitions API over JavaScript animation libraries for page transitions.
Fail:
import { motion } from "motion/react";
function ImageLightbox() {
return (
<motion.img layoutId="hero" /> // JS-based shared element transition
);
}
Pass:
function openLightbox(img: HTMLImageElement) {
img.style.viewTransitionName = "hero";
document.startViewTransition(() => {
// Native browser transition
});
}
transition-style-pseudo-elementsStyle view transition pseudo-elements for custom animations.
Fail:
document.startViewTransition(() => { /* ... */ });
// Uses default crossfade
Pass:
::view-transition-group(card) {
animation-duration: 300ms;
animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
}
native-backdrop-stylingUse ::backdrop pseudo-element for dialog/popover backgrounds.
Fail:
<>
<div className={styles.overlay} onClick={close} />
<dialog className={styles.dialog}>{children}</dialog>
</>
Pass:
dialog::backdrop {
background: var(--black-a6);
backdrop-filter: blur(4px);
}
native-placeholder-stylingUse ::placeholder for input placeholder styling, not wrapper elements.
Fail:
<div className={styles.inputWrapper}>
{!value && <span className={styles.placeholder}>Enter text...</span>}
<input value={value} />
</div>
Pass:
input::placeholder {
color: var(--gray-9);
opacity: 1;
}
native-selection-stylingUse ::selection for text selection styling.
Pass:
::selection {
background: var(--blue-a5);
color: var(--gray-12);
}
When reviewing files, output findings as:
file:line - [rule-id] description of issue
Example:
components/button/styles.module.css:12 - [pseudo-content-required] ::before missing content property
components/lightbox/index.tsx:45 - [transition-over-js-library] Using motion layoutId instead of View Transitions API
After findings, output a summary:
| Rule | Count | Severity |
|---|---|---|
pseudo-content-required | 2 | HIGH |
pseudo-over-dom-node | 1 | MEDIUM |
transition-name-cleanup | 1 | MEDIUM |
Weekly Installs
139
Repository
GitHub Stars
641
First Seen
Jan 26, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
opencode119
cursor115
codex112
claude-code107
gemini-cli106
github-copilot95
Flutter/Dart代码审查最佳实践:提升应用性能与质量的完整检查清单
945 周安装