Loading...
Loading...
Audit CSS for pseudo-element best practices and View Transitions API usage. Use when reviewing hover effects, decorative layers, or page transitions. Outputs file:line findings.
npx skill4agent add raphaelsalaja/userinterface-wiki pseudo-elementsfile:line| Priority | Category | Prefix |
|---|---|---|
| 1 | Before/After | |
| 2 | View Transitions | |
| 3 | Native Styling | |
pseudo-content-required.button::before {
position: absolute;
background: var(--gray-3);
}.button::before {
content: "";
position: absolute;
background: var(--gray-3);
}pseudo-over-dom-node<button className={styles.button}>
<span className={styles.background} /> {/* Unnecessary DOM node */}
Click me
</button><button className={styles.button}>
Click me
</button>.button::before {
content: "";
/* decorative background */
}pseudo-position-relative-parent.button::before {
content: "";
position: absolute;
inset: 0;
}
/* .button has no position */.button {
position: relative;
}
.button::before {
content: "";
position: absolute;
inset: 0;
}pseudo-z-index-layering.button::before {
content: "";
position: absolute;
inset: 0;
background: var(--gray-3);
}
/* Covers button text */.button {
position: relative;
z-index: 1;
}
.button::before {
content: "";
position: absolute;
inset: 0;
background: var(--gray-3);
z-index: -1;
}pseudo-hit-target-expansion<div className={styles.wrapper}> {/* Extra wrapper for hit target */}
<a className={styles.link}>Link</a>
</div>.link {
position: relative;
}
.link::before {
content: "";
position: absolute;
inset: -8px -12px;
}transition-name-requireddocument.startViewTransition(() => {
// No view-transition-name assigned
targetImg.src = newSrc;
});sourceImg.style.viewTransitionName = "card";
document.startViewTransition(() => {
sourceImg.style.viewTransitionName = "";
targetImg.style.viewTransitionName = "card";
});transition-name-unique.card {
view-transition-name: card;
}
/* Multiple cards with same name */// Assign unique name only to transitioning element
element.style.viewTransitionName = `card-${id}`;transition-name-cleanupsourceImg.style.viewTransitionName = "card";
document.startViewTransition(() => {
targetImg.style.viewTransitionName = "card";
});
// sourceImg still has name, causes conflict on next transitionsourceImg.style.viewTransitionName = "card";
document.startViewTransition(() => {
sourceImg.style.viewTransitionName = "";
targetImg.style.viewTransitionName = "card";
});transition-over-js-libraryimport { motion } from "motion/react";
function ImageLightbox() {
return (
<motion.img layoutId="hero" /> // JS-based shared element transition
);
}function openLightbox(img: HTMLImageElement) {
img.style.viewTransitionName = "hero";
document.startViewTransition(() => {
// Native browser transition
});
}transition-style-pseudo-elementsdocument.startViewTransition(() => { /* ... */ });
// Uses default crossfade::view-transition-group(card) {
animation-duration: 300ms;
animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
}native-backdrop-styling<>
<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<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 {
background: var(--blue-a5);
color: var(--gray-12);
}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| Rule | Count | Severity |
|---|---|---|
| 2 | HIGH |
| 1 | MEDIUM |
| 1 | MEDIUM |