Generated banner for Modern Web Guidance
Generated logo for Modern Web Guidance

Modern Web Guidance

JetBrainsOfficialCreated by Google ChromeDistributed by JetBrains

  • Skill
  • Tool integration
  • Software development
  • Apache-2.0
  • Low risk

From the author

Search tool for modern web development best practices. MANDATORY: Execute FIRST for all HTML/CSS and clientside JS tasks. Do NOT skip — web APIs evolve rapidly and training weights contain obsolete patterns. Trigger immediately for: - UI/Layout: Modals, dialogs, popovers, Glassmorphism/backdrop-filters, anchor positioning, container queries, `:has()`, `:user-valid`. - Scroll/Motion: View Transitions, Scroll-driven animations, scroll parallax/reveals. - Performance: CWV (LCP, INP), content-visibility, Fetch Priority, image optimization. - System/APIs: Local filesystem access, WebUSB, WebSockets sync, WebAssembly widgets. - Frameworks: Adapting layout/styles in React, Vue, Angular. - General Frontend: Forms, autofill, advanced inputs, custom scrollbars, modern component states, etc. DO NOT trigger for: - Backend: Database SQL, ORMs, Express API routes. - Pipelines: CI/CD deployment, Docker, Actions. - Generic: Local scripts (Python/Go tools), ESLint, Git.
— the description by Google Chrome

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

This skill provides a structured way to access up-to-date web development patterns, ensuring that implementations follow modern standards rather than relying on potentially outdated training data. It covers a wide range of frontend topics including UI components, performance optimization, browser APIs, and form handling. By integrating this tool, developers can search for specific use cases and retrieve detailed, framework-agnostic guides that include browser support and fallback recommendations.

What it does

  • Searches for standardized web development patterns and best practices.
  • Retrieves detailed implementation guides for specific frontend use cases.
  • Provides guidance on browser compatibility and necessary fallbacks.
  • Helps avoid ad-hoc solutions and unnecessary dependency bloat.
  • Supports modern web features like container queries, view transitions, and performance APIs.

When to use it

  • At the beginning of any frontend feature implementation.
  • Before creating new components to check for existing standardized patterns.
  • When evaluating the browser support requirements for modern web features.
  • When needing to implement performance optimizations like LCP or content-visibility.

Advantages

  • Ensures use of current, recommended web development practices.
  • Reduces technical debt by discouraging ad-hoc implementations.
  • Provides clear guidance on handling browser compatibility and fallbacks.
  • Promotes the use of native browser capabilities over heavy external dependencies.

Usage examples

“I need to implement a modal using modern web standards. What is the best approach?”
The agent searches for modal-related guides and retrieves the recommended implementation pattern, including any necessary browser fallbacks.
“How can I optimize the loading priority of my LCP images?”
The agent searches for image optimization guides and retrieves the specific instructions for using fetch priority to improve performance.

Install

  1. Download the skill as a ZIP:

    https://autogineer.com/agent-skills/jetbrains/modern-web-guidance/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip modern-web-guidance.zip -d .agents/skills

Documentation by Google Chrome

Modern Web Guidance

A skill to search for specific web development use cases and retrieve their corresponding best practice guides.

When to use

Must use this skill:

  • At the start of implementing any web feature.

  • Before creating a new component, to check if a standardized pattern already exists.

  • To avoid implementing ad-hoc solutions or loading large dependencies unnecessarily.

Usage Instructions

Step 1. Search Use Cases

Search with an action-oriented query summarizing what you want to achieve using the search command. Run modern-web-guidance directly with npx.

npx -y modern-web-guidance@latest search "<query>" --skill-version 2026_05_16-c5e7870

Example Output:

[
  {
    "id": "optimize-image-priority",
    "description": "Optimize the loading priority of Largest Contentful Paint (LCP) candidate images.",
    "category": "performance",
    "featuresUsed": [ "Fetch priority" ],
    "tokenCount": 985,
    "similarity": 0.7289
  },
  {
    "id": "defer-rendering-heavy-content",
    "description": "Reduce rendering times in content-heavy web pages by deferring rendering for offscreen content.",
    "category": "performance",
    "featuresUsed": [ "content-visibility", "hidden=\"until-found\"" ],
    "tokenCount": 1250,
    "similarity": 0.6961
  }
]

Note: If search results are vague, return no matches, or show low similarity scores, run the list command to browse all guides:

npx -y modern-web-guidance@latest list

Step 2. Retrieve Best Practices

Once you have a relevant id from the search results, call this script using the retrieve command to get the full guide. You can pass multiple IDs separated by commas.

npx -y modern-web-guidance@latest retrieve "<id>"

Example Output: The markdown content of the guide describing implementation steps...

Using npx

  • IMPORTANT: on Windows, using npx may fail. Use npx.cmd ... instead.

  • Network access is required for fetching npm packages needed by the task.

  • If the npx -y modern-web-guidance… command hangs, you may be offline. Try running again in offline mode: npx --offline ….

  • The --skill-version flag is used to determine if this SKILL.md is out of date. If it is, a warning message is logged to stderr.

Guidelines

  • Always search first to find the most relevant guides.

  • These guides are usually framework-agnostic; adapt them correctly to your setup.

  • Do not hallucinate guides or ignore them; they represent the preferred local standard for the user's project.

Interpreting Browser Support & Fallbacks

  • Default Behavior: All guides assume Baseline Widely available features are safe to use without fallbacks. For features that are not Baseline widely available, you MUST follow the fallback recommendations in the guide, unless the user has specified a custom browser support policy.

  • Custom Policies: If the user has already defined explicit browser support requirements, use the browser compatibility data in the guide to determine if a fallback can be safely ignored.

    • For Baseline YYYY targets, a feature satisfies this target if its "Baseline since" date is <= YYYY.

    • Policy Examples:

      • "Do not implement feature fallbacks." (for exploratory prototypes of the cutting-edge web)

      • "Safari 17.4+" (for internal tools targeting macOS or Tauri-based desktop apps)

      • "Never recommend or implement polyfills; if a Baseline Newly Available feature is required for core functionality, provide a lightweight custom fallback or redesign the approach." (to minimize bundle size and avoid technical debt)

      • "Assume a modern execution environment where Baseline Newly Available features can be used natively, provided they are strictly feature-detected and degrade gracefully." (for progressive enhancement strategies)

  • Reactive Policy Discovery: Watch for environmental cues to suggest documenting a policy in CLAUDE.md or AGENTS.md. Suggest this if the developer:

    • Mentions building for a restricted runtime (e.g., Electron or Tauri).

    • Explicitly excludes specific targets (e.g., "we don't support Desktop Chrome").

    • Expresses hesitation about polyfill complexity, bundle size, or performance cost.

    • Questions if a feature is safe to use without fallbacks.

    No defined policy format. This is an example: **Browser Support:** Allow Newly Available features, but only adopt custom fallback code that adds <= 20 lines and does not require external dependencies.

Files

guides/css/css.md

CSS: Modern Architecture and Performance

These guidelines provide a high-density reference for writing maintainable, performant, and standard-compliant CSS.

  1. 1. Foundations

  2. 2. Inheritance and The Cascade

  3. 3. Selectors and scoping

    1. Prefer CSS selectors over JS for complex element targeting

    2. Use :is() (or :where()) instead of CSS rule duplication for fallbacks

    3. Avoid overmatching

    4. Nesting and scoping

  4. 4. Interactivity

    1. Focus management

    2. Touch targets

  5. 5. Design Tokens and Theming

    1. Dark mode

    2. Forced Colors Mode

    3. Generating tints

    4. Theming browser-generated UI

  6. 6. Responsive design

    1. Responsive Typography

  7. 7. Typography

    1. Text wrapping

  8. 8. Visual effects

    1. Depth and texture

    2. Shapes

    3. Gradients and color-mix()

    4. Patterns

  9. 9. Transitions & animations

    1. Performance

    2. Accessibility

  10. 10. Generated content

1. Foundations

Be allergic to knowledge duplication. Prefer variables over repetition, but whenever possible, prefer built-in conventions such as:

  • currentColor instead of defining a variable and setting color to it

  • The inherit keyword instead of defining a variable on the parent and using it on the same property across parent and child.

  • em units instead of font-size: var(--size)

  • cqw/cqh (or their logical versions — cqi/cqb) units instead of repeating box model values.

  • Code duplication is not knowledge duplication. The goal is robustness and maintainability, not saving characters.

  • Prefer logical properties and values over physical ones (e.g. margin-inline-start instead of margin-left) so that styles adapt to different writing modes and orientations. Even if the page author does not plan to localize, external translation tools often display translated text in context.

  • Do not use logical properties indiscriminately — ask yourself "would I want this to flip in RTL?" — if the answer is no, use the physical property instead.

  • Consider different viewing modes (dark mode, high contrast mode), different viewport sizes, and different input modes (touch, keyboard, pointer).

2. Inheritance and The Cascade

Avoid introducing BEM naming conventions to manage specificity. Instead, use modern CSS features such as cascade layers and :where() to make cascade behavior predictable and follow author intent.

Use cascade layers (@layer) to define explicit priority zones (e.g., reset, base, theme, components, utilities), and declare their order upfront (e.g. @layer reset, base, theme, components, utilities;). Within each layer, use :where() to make selectors only compete based on meaningful signals, not incidental filters (:not() edge cases, remote ancestors, etc.) or for one-off easily overridable defaults.

Use keywords like inherit, initial, unset, or revert instead of explicit values to improve maintainability and better express intent. Examples:

  • When specifying a transition on a child that should match the parent's transition-* properties, instead of repeating the transition properties on the child, use transition: inherit (reduce duplication, improve maintainability)

  • Use initial to reset a property to its initial value instead of specifying the value explicitly (clearer expression of intent)

3. Selectors and scoping

Modern browser-native selectors reduce the need for preprocessors and complex state-tracking in JS.

Prefer CSS selectors over JS for complex element targeting
  • DO use :has() to style parents based on child state instead of managing classes in JS (e.g. label:has(:checked) instead of a manual label.has-checked class) For more information, see the guides at child-state-based-styling (via npx -y modern-web-guidance@latest retrieve "child-state-based-styling") and content-based-styling (via npx -y modern-web-guidance@latest retrieve "content-based-styling").

  • DO NOT nest :has() or use pseudo-elements inside it (browser API limitation)

  • Use :nth-child(<An+B> of <selector>) when you need to style every n-th element of a certain type. E.g. details:nth-child(1 of [open]) will style the first open <details> element it finds, whereas details[open]:first-child would style only the first child if and only if it was open.

Use :is() (or :where()) instead of CSS rule duplication for fallbacks

DO NOT duplicate CSS rules to provide fallbacks for pseudo-classes that may not be supported — use :is() or :where() instead and take advantage of their forgiving parsing rules.

/* BAD: duplicate rules instead of using `:where()` */
[popover]:popover-open {
  /* styles for native popovers */
}
[popover].\:popover-open {
  /* same styles again, for polyfilled popovers */
}

/* GOOD */
[popover]:where(:popover-open, .\:popover-open) {
  /* same styles in one rule */
}

Do NOT use this for pseudo-elements, as they are not supported in :is() or :where().

Avoid overmatching

Write selectors in a way that expresses intent.

Use :not() instead of overrides to exclude irrelevant states/targets

When the intent is to exclude certain states or elements that are fundamentally irrelevant, use :not().

For example, to apply bottom borders between list items, don't do this:

.fancy-list li {
  border-bottom: 1px solid silver;
}

.fancy-list li:last-child {
  border-bottom: none;
}

This can unintentionally overwrite a desirable border-bottom set from another rule. The actual intent was to only apply the bottom border to the non-last lis. The code above is a workaround that poorly expresses this intent. Instead, this expresses intent more clearly:

.fancy-list li:not(:last-child) {
  border-bottom: 1px solid silver;
}

Similarly, don't do this:

button:hover {
  background: var(--color-blue);
}

button:disabled {
  background: var(--color-neutral);
}

If we reorder the two rules, we will get a hover background on disabled buttons! Instead, do this:

button:hover:not(:disabled) {
  background: var(--color-blue);
}

button:disabled {
  background: var(--color-neutral);
}

This works regardless of reordering, as the first rule does not overmatch.

Prefer @scope over :not() for excluding (potentially deeply nested) subtrees

While :not() + descendant selectors can exclude subtrees, this works poorly for deeply nested structures. For example, .card :not(.content *) will not work as expected for nested cards. @scope fixes this as it takes hierarchical proximity into account:

@scope (.card) to (.content) {
  /* styles for elements inside .card but not inside .content */
}

This will work as expected even for nested cards.

Overrides are fine for specialization

This is fine:

button {
  background: var(--color-neutral);
}

button.primary {
  background: var(--color-blue);
}

Both rules express legitimate intent: buttons are generally neutral, but primary ones are blue.

No global resets

DO NOT use global resets (styles on *) as they cannot be overridden by web components or lower-priority cascade layers (without !important). Instead, apply reset styles to specific element types and/or conditions.

Nesting and scoping

Use native CSS nesting to group related styles to the extent it improves maintainability and readability.

Prefer @scope over nesting when proximity should matter more than pure specificity. This is common in selectors that can be nested in any order, but the closest matching one (in element -> ancestor order) should win, e.g. theming classes.

For example this will not work as expected:

.dark .invert { color-scheme: light }
.light .invert { color-scheme: dark }

If .invert is nested within both .dark and .light, it will always resolve to dark mode as both rules have the same specificity. Using @scope fixes this:

@scope (.dark) {
  .invert { color-scheme: light }
}

@scope (.light) {
  .invert { color-scheme: dark }
}

4. Interactivity

Focus management
  • Use :focus-visible to define custom focus rings, not :focus.

  • Do not remove the browser's default focus rings (via outline: none) without providing an alternative visible focus style.

  • Prefer outline over other properties (e.g. box-shadow) for focus rings. If you must rely on box-shadow for focus rings, provide an outline-based fallback for High Contrast Mode using the forced-colors media query.

  • Pair focus outlines with outline-offset to visually separate the ring from the element.

Touch targets
  • Interactive elements should be at least 24×24 CSS pixels (WCAG 2.5.8 AA). Enforce with min-block-size / min-inline-size or padding rather than width / height, so content can grow the target but not shrink it.

  • Bump targets up on coarse pointers: @media (pointer: coarse) { ... }.

  • DON'T use touch-action: none for custom gestures — it disables page scrolling through the element. Scope to the axis you actually need: pan-y for horizontal swipes (page still scrolls vertically), pan-x for vertical ones. Reserve none for elements where no native touch behavior makes sense (e.g. a drawing canvas).

5. Design Tokens and Theming

Use CSS custom properties on :root to define core design variables (colors, fonts, sizes, etc) used throughout the design, for visual consistency and to scale UI design across teams. DO NOT specify nontrivial styling values inline. E.g. background: transparent or padding: 0 is ok, but background: #f06 or padding: .3em are not. One exception is use cases where keeping code small and simple is far more important than long-term maintainability and evolution, such as testcases.

Typically these are organized in tiers, with each tier building upon the previous one. For example:

  1. Tier 1: Literal design tokens (e.g. --color-blue-10, --color-gray-90, --font-sans-serif, --size-xl etc)

  2. Tier 2: Semantic design tokens (e.g. --color-accent, --color-neutral, --font-body, --font-heading etc)

  3. Tier 3: General UI design tokens (e.g. --ui-border, --surface-bg-subtle etc)

  4. Tier 4: Component-specific design tokens (e.g. --button-bg-primary-hover, --button-border-color-secondary etc)

The smaller the scope of the use case, the fewer tiers it needs. E.g. a quick demo or toy app are fine with one tier. Do not overengineer. Check for any existing conventions around naming and levels before inventing your own.

Dark mode
  • Use color-scheme: light dark on :root to enable dark mode support that automatically adapts to the system setting. You can also specify color-scheme on individual elements to force a different value for that subtree (light/dark or light dark for the system default)

  • Use light-dark() to provide alternatives that automatically resolve based on the element's color-scheme. Typically this happens in Tier 2 or Tier 3 tokens.

  • IMPORTANT: When using light-dark() on an inherited <color> property, it will resolve to a specific color based on that element's color-scheme and inherit as that resolved color, not as a light-dark() value. It will NOT adapt to any descendant-specific color-scheme overrides. To keep light-dark() color tokens dynamic resolve them as late as possible by only passing them around as unregistered custom properties and avoid relying on inherited color values across color-scheme boundaries.

See dark-mode (via npx -y modern-web-guidance@latest retrieve "dark-mode") for tips & best practices on supporting dark mode switching and component-specific-light-dark-theme (via npx -y modern-web-guidance@latest retrieve "component-specific-light-dark-theme") for more on applying different color-scheme modes than the page-wide setting on certain elements.

Forced Colors Mode

In Forced Colors Mode (High Contrast on Windows), the browser overrides author colors with system keywords and strips background-image, box-shadow, and border-image.

  • Define system color fallbacks for color tokens using @media (forced-colors: active).

  • DON'T rely on background-image, box-shadow, or border-image to convey borders, separators, or state — they disappear in forced colors (and often in print too). If you must, ensure there's an alternative in forced colors mode, such as outline or border with system color keywords (CanvasText, LinkText, ButtonText, Highlight, GrayText, etc.).

  • Use forced-color-adjust: none where color is essential information (syntax highlighter, color picker swatch). DON'T use forced-color-adjust: none just to preserve aesthetics.

Generating tints

Before generating tints dynamically, check if you can use an existing, predefined, design token. This allows much more designer control and ensures consistency.

If you need to generate lighter or darker colors dynamically:

  • DO NOT just adjust the lightness channel in oklch/oklab or lch/lab, e.g. oklab(from var(--primary) 0.9 a b). While that is theoretically the correct way, browsers do not yet implement gamut mapping, so the resulting color is unpredictable.

  • You can use color-mix() to mix with white or black (preferably in oklab). This keeps the color safely in gamut, but tends to over-desaturate colors and produce washed out tints and shades.

  • You MAY combine lightness adjustment with any of the other methods (e.g. color-mix(in oklab, oklch(from var(--primary) 0.9 c h), white 30%)) for a balance between the two, but avoid going above 30% for the lightness adjustment.

Theming browser-generated UI

Most browser-generated UI can be customized to some extent using CSS. Even if it requires modern features, it degrades gracefully in older browsers, and thus often does not require a polyfill or fallback.

Before re-creating browser UI (form controls, scrollbars, selections, error messages, etc), first verify that:

  1. the browser UI cannot be customized enough for your needs, even with modern CSS,

  2. the desired customization is sufficiently critical to justify the tradeoffs of re-creating built-in UI — most notably losing accessible semantics, keyboard handling, IME, and AT integration that the native UI provides for free.

Example customizations that are possible:

  • Use ::selection to customize highlighted text colors.

  • DON'T apply user-select: none to content text — breaks copy-paste, translation tools, and AT "read from here" gestures. Limit it to chrome (drag handles, toolbars, redundant button labels).

  • Use accent-color to apply the page's accent color to any browser-generated UI.

  • Use color-scheme to have browser UI adapt to light/dark mode.

  • Use scrollbar-color to customize scrollbar colors and scrollbar-width to control scrollbar thickness — keep the thumb visibly distinct from the track (≥3:1), and don't set scrollbar-width: none on scrollable regions (use it only when scrolling is fully replaced by another affordance).

  • Use :user-invalid / :user-valid for validity styling, not :invalid / :valid — they only match after the user has interacted with the field, avoiding the hostile default of flagging required-empty fields as errors on page load.

  • Buttons and text fields (including <textarea>) can generally be styled as normal elements.

  • Use font-size to scale and other textual properties to control typography

Styling textual fields (<input> & <textarea>)

For most styling purposes (e.g. colors, borders, backgrounds, typography, etc) treat these elements as normal text containers.

  • Use :placeholder-shown and ::placeholder to style input placeholders.

  • Use field-sizing: content to make text fields size to content.

  • For <textarea> elements, use resize: vertical to disable horizontal resizing or resize: none to disable all resizing.

Multiple choice controls (select, radios, checkboxes)
  • To select one among many options presented in a dropdown: Use a <select> + appearance: base-select + ::picker(select). For more info see branded-select-styling (via npx -y modern-web-guidance@latest retrieve "branded-select-styling")

  • Selecting one or more among multiple options laid out inline in the page: Use a <input type=checkbox> or <input type=radio> inside a <label> for each option. Style via label:has(:checked).

  • Style checkboxes, radios and switches via appearance: none + generated content (::before/::after) or background images to draw the checked state.

<!-- Customizable select listbox version currently buggy + this has much better browser support -->

Non-textual <input>s (buttons, sliders, file inputs etc.)
  • File inputs: Use ::file-selector-button to style the button.

  • Do not use <input> with a type of button, submit or reset. Use <button> instead and style it as a regular element.

  • Sliders: Use appearance: none + thumb pseudo-elements (::-webkit-slider-thumb, ::-moz-range-thumb, etc) and track pseudo-elements (::-webkit-slider-runnable-track, ::-moz-range-track, etc) for more granular control.

6. Responsive design

  • Use @container queries to create component-driven responsive layouts that adapt to their parent container's size rather than the viewport.

  • Use dynamic viewport units (dvh, dvw) instead of vh/vw to prevent layout breakage when mobile browser UI elements (like address bars) appear or disappear.

  • Use aspect-ratio for media elements (like <img> and <video>) to reserve space during loading and prevent Cumulative Layout Shift (CLS).

Responsive Typography
  • DO combine viewport-relative and font-relative units in clamp() for font sizes that scale with the viewport size while ensuring they stay within a desired range. For example, clamp(2rem, 1rem + 5vw, 4rem). Adjust the proportion of viewport-relative and font-relative units to control how quickly the font-size changes.

  • DON'T use vw alone for font-size without clamp(), as it can scale text too small or too large on extreme screens.

7. Typography

  • Use unitless numbers for line-height (e.g., 1.5) to ensure relative scaling during font-size inheritance.

  • Use overflow-wrap: break-word (or anywhere) to contain long URLs.

  • DON'T use px for font-size. Prefer rem to honor the user's browser font-size preferences (root font size), or em for contextual sizing.

Text wrapping
  • Use text-wrap: balance for balanced headlines and headline-like content (e.g. <th>)

  • Use text-wrap: pretty for long-form body text (paragraphs, blockquotes, etc.)

  • Use text-wrap: balance or text-wrap: pretty deliberately, DO NOT apply it on * as it does have a performance cost.

  • Avoid text-wrap: balance on elements with a visible box (backgrounds, borders, shadows, etc) as it does not change the container's width, it only affects how text wraps within that width. This can leave empty space at the end of the container, which is usually undesirable.

8. Visual effects

Depth and texture
  • Layer multiple shadows for realistic soft depth effects.

  • Use filter: drop-shadow() instead of box-shadow for non-rectangular shapes or transparent PNGs.

  • Use mix-blend-mode and background-blend-mode for lighting overlays (limit scope with isolation: isolate)

.hero {
  background-image: url('texture.png'), linear-gradient(to bottom, #fff, #eee);
  background-blend-mode: soft-light;
}
Shapes
  • Use corner-shape: squircle for more aesthetically pleasing curves as a progressive enhancement over regular rounded corners.

  • Use elliptical border-radius (e.g., 10px / 20px) for proportional curves without extra elements.

Gradients and color-mix()

Use in oklch or in oklab to explicitly specify the interpolation color space for gradients or color-mix().

  • in oklch preserves chroma better, but can more easily get out of device gamut, especially for bigger differences between colors

  • in oklab stays in gamut more easily (assuming in-gamut endpoints) but can create washed out desaturated colors in the middle, especially when interpolating between opposite hues.

  • DON'T use in srgb unless you have a specific reason to do so (e.g. you are building a color picker that needs to interpolate in srgb).

Fallback

Some pre-2024 browsers do not support gradient color interpolation space. To support these browsers, use the token only when its usage is safe by defining a variable:

:root {
  --in-oklab: ;
  --in-oklch: ;
}

@supports (linear-gradient(in oklab, white, black)) {
  :root {
    --in-oklab: in oklab;
    --in-oklch: in oklch;
  }
}

Then use like:

.card {
  background: linear-gradient(to bottom var(--in-oklab), var(--accent-color), var(--darker));
}
  • Important: If you use this technique, make sure there is always a non-empty gradient preamble without it, otherwise it will be a syntax error in older browsers.

  • You do NOT need this for color-mix(). If a browser supports color-mix(), it also supports its in <color-space> argument.

Patterns

Many patterns can be created via CSS gradients + hard stops, and these can be more flexible and performant than SVGs or external images as they can have access to CSS variables and lengths from the surrounding context. You don't need to repeat the position twice — just use 0 or 0% and gradient fixup will auto-adjust it.

Examples below.

Vertical stripes of 1em width each:

background: linear-gradient(to right, var(--color-1) 50%, var(--color-2) 0) 0 / 2em;

Diagonal stripes of 1em width each:

background: repeating-linear-gradient(-45deg, var(--color-1) 0 1em, var(--color-2) 0 2em);

Checkerboard pattern with 1em squares:

background: repeating-conic-gradient(var(--color-1) 0 25%, var(--color-2) 0 50%) 0 / 2em 2em;

Polka dot with .5em radius dots spaced 2em apart (horizontally/vertically — multiply by sqrt(2) for diagonal distance):

--distance: 2em;
--radius: .5em;
--polka: radial-gradient(circle, var(--color-1) var(--radius), transparent calc(var(--radius) + 1px));
background: var(--polka) 0 0, var(--polka) var(--distance) var(--distance) var(--color-2);
background-size: calc(var(--distance) * 2) calc(var(--distance) * 2);

Simple pie chart:

.pie {
  --p: 80%;
  width: 60px;
  aspect-ratio: 1;
  border-radius: 50%;
  background: conic-gradient(var(--color-1) var(--p), transparent 0%) var(--color-2);
}

Important: When using gradients to render charts, ensure there is a textual fallback for screen readers. MANDATORY: You MUST provide a semantic data table as an accessible alternative, as detailed in accessibility (via npx -y modern-web-guidance@latest retrieve "accessibility") under the alternate text and media guidelines.

9. Transitions & animations

  • Use clip-path and mask-image for custom geometric reveals and smooth fade-outs.

  • Use Scroll-Driven Animations (animation-timeline: scroll()) for non-essential scroll-bound effects instead of JS listeners.

  • Use View Transitions to animate between complex layout states seamlessly.

Performance

Rendering performance is critical for smooth user experiences, especially in heavy DOM trees.

  • Prefer to animate opacity and transform (including individual transform properties, e.g. translate instead of left/right/top/bottom) to ensure animations stay on the compositor thread.

  • Use transition-behavior: allow-discrete + @starting-style to animate layout properties like display or <dialog> state natively.

  • Always pair content-visibility with contain-intrinsic-size to prevent scrollbar jumps (CLS).

  • When setting contain-intrinsic-size use the auto keyword and a value that’s derived from what is known about the contents (i.e. text size, spacing, size of graphics, character count). Preferably use units such as rem, lh, cap, or ch that match values used for the elements within the contents rather than px. If the content for items in a group is not consistently sized, then use an average size.

  • Use contain: layout style paint to isolate component rendering updates.

Code Example: Render Optimization
.large-section {
  content-visibility: auto;
  contain-intrinsic-block-size: auto 800px;
}

.row {
  --row-gap: .4rem;
  --title-height: 1lh;
  --description-height: 0.85lh;

  display: grid;
  row-gap: var(--row-gap);
  content-visibility: auto;
  /* The sum of the title height, row gap, and description height should be the size of the contents when skipped for rendering. */
  contain-intrinsic-block-size: auto calc(var(--title-height) + var(--row-gap) + var(--description-height));
}

.popover-reveal {
  /* Allow discrete animations for display transitions */
  transition: display 0.2s allow-discrete;
}
Accessibility

Use prefers-reduced-motion media queries to turn off heavy motion for users who prefer it.

DO NOT globally apply animation-duration: 0.01ms; globally as it can cause certain animations to become more jarring. Either apply reduced motion versions on a case by case basis, or use a custom property like:

@property --animation-reduced {
  syntax: "*";
  inherits: false;
  initial-value: none;
}

@media (prefers-reduced-motion: reduce) {
  * {
    animation: var(--animation-reduced) !important;
  }
}

Then, reduced motion versions can be kept together with the original animations:

progress:not([value]) {
  animation: slide 1s infinite linear;
  --animation-reduced: slide 20s infinite linear;
}

10. Generated content

  • DON'T use content to convey meaningful text (labels, state, instructions) — keep that in the DOM (WCAG F87). The alt text argument is harm reduction for cases where decoration accidentally carries meaning, not a license.

  • Use the alternative text argument of content to provide alt text for screen readers. E.g. content: url(cloud.svg) / "Save";

  • Use content: "text" / ""; to prevent purely decorative text from being announced to screen readers.

  • DON'T use an empty alt text argument for images — they're already presentational by default. E.g. this is wrong: content: url(cloud.svg) / "";.

  • DON'T use the alt text argument to describe emojis unless the description differs from the official emoji name. E.g. don't do content: "🎉" / "celebration";, but content: "🎉" / "Yay!"; is fine.

ONLY use the alt text argument when the text is different than the primary value and is not already present in the DOM. I.e. this is wrong:

HTML:

<button class="save">Save</button>

CSS:

button.save::before {
  content: url(cloud.svg) / "Save";
}

A screen reader would read it out as "Save save".

Requirements and compatibility

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Needs: Needs Node.js

Security

What our scanner noticed· Low risk

  • Instructions that use the network

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Created by
Google Chrome
Publisher
JetBrains (Official)
Distributed by
JetBrains
Source
JetBrains/skills · modern-web-guidance · commit 3f53782
Licence
Apache License 2.0 (redistribution allowed)
  • ATTRIBUTION.md (written by AutoGineer)
Synced

AutoGineer is not affiliated with or endorsed by Google Chrome or JetBrains.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download modern-web-guidance.zip