Modern Web Guidance
JetBrainsOfficialCreated by Google ChromeDistributed by JetBrains
- Skill
- Tool integration
- Software development
- Apache-2.0
- Low risk
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
Download the skill as a ZIP:
https://mp-staging.autogineer.com/agent-skills/jetbrains/modern-web-guidance/downloadUnzip 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-c5e7870Example 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
listcommand 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
npxmay fail. Usenpx.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-versionflag 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
- ATTRIBUTION.mdAdded by AutoGineer939 B
- LICENSE11.1 KiB
- SKILL.md5.7 KiB
- guides/accessibility/accessibility.md24.0 KiB
- guides/accessibility/accessible-error-announcement.md6.9 KiB
- guides/built-in-ai/language-detection.md3.5 KiB
- guides/built-in-ai/language-model.md7.1 KiB
- guides/built-in-ai/summarizer.md5.9 KiB
- guides/built-in-ai/translator.md5.3 KiB
- guides/css-layout/css-layout.md19.5 KiB
- guides/css/css.md27.0 KiB
- guides/css/highlight-text-ranges.md5.4 KiB
- guides/forms/animated-select-picker.md7.8 KiB
- guides/forms/autofill-address-form.md4.1 KiB
- guides/forms/autofill-highlight-inputs.md2.3 KiB
- guides/forms/autofill-payment-form.md8.5 KiB
- guides/forms/autofill-sign-in-form.md9.9 KiB
- guides/forms/autofill-sign-up-form.md11.0 KiB
- guides/forms/brand-consistent-forms.md8.3 KiB
- guides/forms/branded-select-styling.md5.5 KiB
- guides/forms/custom-select-picker-layouts.md5.0 KiB
- guides/forms/form-fields-automatically-fit-contents.md5.8 KiB
- guides/forms/forms.md13.8 KiB
- guides/forms/required-field-feedback.md6.8 KiB
- guides/forms/rich-media-picker.md6.6 KiB
- guides/forms/select-menu-interaction.md5.6 KiB
- guides/forms/validate-input-after-interaction.md8.6 KiB
- guides/html/html.md17.5 KiB
- guides/passkeys/passkey-authentication.md9.7 KiB
- guides/passkeys/passkey-conditional-create.md6.5 KiB
- guides/passkeys/passkey-management.md9.3 KiB
- guides/passkeys/passkey-reauthentication.md4.9 KiB
- guides/passkeys/passkey-registration.md9.3 KiB
- guides/passkeys/passkeys.md4.2 KiB
- guides/performance/batch-analytics-events.md6.4 KiB
- guides/performance/break-up-long-tasks.md2.1 KiB
- guides/performance/calculate-total-foreground-time.md2.9 KiB
- guides/performance/conditional-async-dependencies.md3.6 KiB
- guides/performance/defer-rendering-heavy-content.md6.3 KiB
- guides/performance/defer-work-until-scroll-ends.md3.3 KiB
- guides/performance/deprioritize-background-fetches.md1.8 KiB
- guides/performance/detect-initial-visibility-state.md4.5 KiB
- guides/performance/efficient-background-processing.md5.0 KiB
- guides/performance/faster-spa-view-transitions.md3.4 KiB
- guides/performance/full-session-analytics.md5.7 KiB
- guides/performance/identify-heavy-scripts.md4.2 KiB
- guides/performance/identify-inp-causes.md5.4 KiB
- guides/performance/improve-next-page-load-performance.md7.1 KiB
- guides/performance/interactions-in-complex-layouts.md3.4 KiB
- guides/performance/optimize-image-priority.md4.0 KiB
- guides/performance/optimize-preload-priority.md3.3 KiB
- guides/performance/optimize-script-priority.md2.6 KiB
- guides/performance/performance.md17.9 KiB
- guides/performance/resolution-optimized-pseudo-elements.md2.3 KiB
- guides/performance/schedule-tasks-by-priority.md2.2 KiB
- guides/performance/sequence-distributed-events.md3.5 KiB
- guides/privacy/privacy.md11.0 KiB
- guides/security/security.md25.2 KiB
- guides/user-experience/adapt-scrollbar-to-contrast-preferences.md3.5 KiB
- guides/user-experience/anchor-positioning-tab-underline.md2.9 KiB
- guides/user-experience/animate-element-entry-exit.md5.0 KiB
- guides/user-experience/animate-to-from-top-layer.md5.5 KiB
- guides/user-experience/animate-to-intrinsic-sizes.md5.3 KiB
- guides/user-experience/apply-webgl-shaders.md9.9 KiB
- guides/user-experience/calculate-event-differentials.md4.4 KiB
- guides/user-experience/calculate-with-intrinsic-sizes.md9.6 KiB
- guides/user-experience/capture-location-agnostic-data.md3.6 KiB
- guides/user-experience/carousel-slide-effects.md8.1 KiB
- guides/user-experience/carousel-snap-highlights.md5.7 KiB
- guides/user-experience/child-state-based-styling.md4.8 KiB
- guides/user-experience/complex-shapes.md3.5 KiB
- guides/user-experience/component-specific-light-dark-theme.md9.3 KiB
- guides/user-experience/consistent-cross-document-transitions.md11.8 KiB
- guides/user-experience/content-based-styling.md5.2 KiB
- guides/user-experience/coordinate-global-events.md3.5 KiB
- guides/user-experience/cross-document-transitions.md4.4 KiB
- guides/user-experience/customize-scrollbar-color-and-thickness.md2.4 KiB
- guides/user-experience/dark-mode.md15.2 KiB
- guides/user-experience/declarative-button-actions.md5.4 KiB
- guides/user-experience/declarative-dialog-popover-control.md9.2 KiB
- guides/user-experience/deliver-optimized-decorative-images.md2.8 KiB
- guides/user-experience/design-token-reactivity.md5.7 KiB
- guides/user-experience/directional-navigation-transitions.md4.9 KiB
- guides/user-experience/dynamic-sibling-animations.md2.6 KiB
- guides/user-experience/dynamic-sibling-styling.md3.6 KiB
- guides/user-experience/export-html-media-from-canvas.md11.2 KiB
- guides/user-experience/expose-canvas-content-to-browser-features.md14.1 KiB
- guides/user-experience/flicker-free-client-side-ab-testing.md8.5 KiB
- guides/user-experience/fluid-scaling.md3.0 KiB
- guides/user-experience/format-human-readable-durations.md4.2 KiB
- guides/user-experience/group-element-transitions.md5.0 KiB
- guides/user-experience/improve-text-layout-and-legibility.md5.1 KiB
- guides/user-experience/individual-transform-properties.md3.6 KiB
- guides/user-experience/interactive-content-in-3d-scenes.md13.3 KiB
- guides/user-experience/interactive-content-reveal.md5.1 KiB
- guides/user-experience/interest-triggered-action-previews.md4.1 KiB
- guides/user-experience/interest-triggered-tooltips.md6.8 KiB
- guides/user-experience/light-dismiss-a-dialog.md3.6 KiB
- guides/user-experience/manage-recurring-intervals.md3.3 KiB
- guides/user-experience/model-partial-time-concepts.md4.2 KiB
- guides/user-experience/move-dom-element-without-losing-state.md2.5 KiB
- guides/user-experience/navigation-drawer.md15.7 KiB
- guides/user-experience/overflow-clipping-control.md8.0 KiB
- guides/user-experience/parallax-scroll-effects.md8.8 KiB
- guides/user-experience/persistent-app-tours.md3.9 KiB
- guides/user-experience/persistent-toast-notifications.md1.8 KiB
- guides/user-experience/persistent-top-layer-ui.md2.2 KiB
- guides/user-experience/physics-based-easing.md5.8 KiB
- guides/user-experience/platform-controls-dismiss-dialog.md2.6 KiB
- guides/user-experience/position-aware-tooltips.md5.9 KiB
- guides/user-experience/precise-text-alignment.md4.7 KiB
- guides/user-experience/prevent-text-wrapping.md2.4 KiB
- guides/user-experience/pull-to-reveal.md4.7 KiB
- guides/user-experience/reduce-style-repetition.md3.9 KiB
- guides/user-experience/resilient-context-menus-and-nested-dropdowns.md4.9 KiB
- guides/user-experience/same-document-transitions.md8.3 KiB
- guides/user-experience/scroll-entry-exit-effects.md7.8 KiB
- guides/user-experience/scroll-position-aware-elements.md3.6 KiB
- guides/user-experience/scroll-progress-indicator.md6.7 KiB
- guides/user-experience/scroll-snap-realtime-feedback.md6.7 KiB
- guides/user-experience/scroll-snap-state-sync.md3.9 KiB
- guides/user-experience/scroll-target-on-load.md3.3 KiB
- guides/user-experience/scrollability-affordance-hints.md3.6 KiB
- guides/user-experience/scrollytelling.md8.3 KiB
- guides/user-experience/search-hidden-content.md6.7 KiB
- guides/user-experience/shaped-cutouts.md7.0 KiB
- guides/user-experience/shrinking-header-on-scroll.md6.3 KiB
- guides/user-experience/size-aware-styling.md2.9 KiB
- guides/user-experience/soft-edge-content-fade.md2.6 KiB
- guides/user-experience/stabilize-reactive-state.md3.5 KiB
- guides/user-experience/stack-drill-down.md32.9 KiB
- guides/user-experience/style-parent-with-has.md6.0 KiB
- guides/user-experience/support-global-calendar-systems.md5.7 KiB
- guides/user-experience/swipe-to-remove.md26.2 KiB
- guides/user-experience/visually-stable-font-fallbacks.md2.9 KiB
- guides/user-experience/visually-stable-mixed-fonts.md2.8 KiB
- guides/user-experience/visually-texture-content.md3.7 KiB
- guides/webmcp/agentic-forms.md5.0 KiB
- guides/webmcp/agentic-javascript-tools.md4.1 KiB
- guides/webmcp/webmcp.md3.7 KiB
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. Foundations
2. Inheritance and The Cascade
3. Selectors and scoping
Prefer CSS selectors over JS for complex element targeting
Use
:is()(or:where()) instead of CSS rule duplication for fallbacksAvoid overmatching
Nesting and scoping
4. Interactivity
Focus management
Touch targets
5. Design Tokens and Theming
Dark mode
Forced Colors Mode
Generating tints
Theming browser-generated UI
6. Responsive design
Responsive Typography
7. Typography
Text wrapping
8. Visual effects
Depth and texture
Shapes
Gradients and
color-mix()Patterns
9. Transitions & animations
Performance
Accessibility
10. Generated content
1. Foundations
Be allergic to knowledge duplication. Prefer variables over repetition, but whenever possible, prefer built-in conventions such as:
currentColorinstead of defining a variable and settingcolorto itThe
inheritkeyword instead of defining a variable on the parent and using it on the same property across parent and child.emunits instead offont-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-startinstead ofmargin-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, usetransition: inherit(reduce duplication, improve maintainability)Use
initialto 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 manuallabel.has-checkedclass) For more information, see the guides atchild-state-based-styling(vianpx -y modern-web-guidance@latest retrieve "child-state-based-styling") andcontent-based-styling(vianpx -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, whereasdetails[open]:first-childwould 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-visibleto 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
outlineover other properties (e.g.box-shadow) for focus rings. If you must rely onbox-shadowfor focus rings, provide anoutline-based fallback for High Contrast Mode using theforced-colorsmedia query.Pair focus outlines with
outline-offsetto 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-sizeor padding rather thanwidth/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: nonefor custom gestures — it disables page scrolling through the element. Scope to the axis you actually need:pan-yfor horizontal swipes (page still scrolls vertically),pan-xfor vertical ones. Reservenonefor 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:
Tier 1: Literal design tokens (e.g.
--color-blue-10,--color-gray-90,--font-sans-serif,--size-xletc)Tier 2: Semantic design tokens (e.g.
--color-accent,--color-neutral,--font-body,--font-headingetc)Tier 3: General UI design tokens (e.g.
--ui-border,--surface-bg-subtleetc)Tier 4: Component-specific design tokens (e.g.
--button-bg-primary-hover,--button-border-color-secondaryetc)
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 darkon:rootto enable dark mode support that automatically adapts to the system setting. You can also specifycolor-schemeon individual elements to force a different value for that subtree (light/darkorlight darkfor the system default)Use
light-dark()to provide alternatives that automatically resolve based on the element'scolor-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'scolor-schemeand inherit as that resolved color, not as alight-dark()value. It will NOT adapt to any descendant-specificcolor-schemeoverrides. To keeplight-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 acrosscolor-schemeboundaries.
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, orborder-imageto 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 asoutlineorborderwith system color keywords (CanvasText,LinkText,ButtonText,Highlight,GrayText, etc.).Use
forced-color-adjust: nonewhere color is essential information (syntax highlighter, color picker swatch). DON'T useforced-color-adjust: nonejust 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/oklaborlch/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 inoklab). 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:
the browser UI cannot be customized enough for your needs, even with modern CSS,
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
::selectionto customize highlighted text colors.DON'T apply
user-select: noneto 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-colorto apply the page's accent color to any browser-generated UI.Use
color-schemeto have browser UI adapt to light/dark mode.Use
scrollbar-colorto customize scrollbar colors andscrollbar-widthto control scrollbar thickness — keep the thumb visibly distinct from the track (≥3:1), and don't setscrollbar-width: noneon scrollable regions (use it only when scrolling is fully replaced by another affordance).Use
:user-invalid/:user-validfor 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-sizeto 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-shownand::placeholderto style input placeholders.Use
field-sizing: contentto make text fields size to content.For
<textarea>elements, useresize: verticalto disable horizontal resizing orresize: noneto 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 seebranded-select-styling(vianpx -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 vialabel: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-buttonto style the button.Do not use
<input>with atypeofbutton,submitorreset. 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
@containerqueries 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 ofvh/vwto prevent layout breakage when mobile browser UI elements (like address bars) appear or disappear.Use
aspect-ratiofor 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
vwalone for font-size withoutclamp(), 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(oranywhere) to contain long URLs.DON'T use
pxfor font-size. Preferremto honor the user's browser font-size preferences (root font size), oremfor contextual sizing.
Text wrapping
Use
text-wrap: balancefor balanced headlines and headline-like content (e.g.<th>)Use
text-wrap: prettyfor long-form body text (paragraphs, blockquotes, etc.)Use
text-wrap: balanceortext-wrap: prettydeliberately, DO NOT apply it on*as it does have a performance cost.Avoid
text-wrap: balanceon 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 ofbox-shadowfor non-rectangular shapes or transparent PNGs.Use
mix-blend-modeandbackground-blend-modefor lighting overlays (limit scope withisolation: isolate)
.hero {
background-image: url('texture.png'), linear-gradient(to bottom, #fff, #eee);
background-blend-mode: soft-light;
}Shapes
Use
corner-shape: squirclefor 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 oklchpreserves chroma better, but can more easily get out of device gamut, especially for bigger differences between colorsin oklabstays 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 srgbunless 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 supportscolor-mix(), it also supports itsin <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-pathandmask-imagefor 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
opacityandtransform(including individual transform properties, e.g.translateinstead ofleft/right/top/bottom) to ensure animations stay on the compositor thread.Use
transition-behavior: allow-discrete+@starting-styleto animate layout properties likedisplayor<dialog>state natively.Always pair
content-visibilitywithcontain-intrinsic-sizeto prevent scrollbar jumps (CLS).When setting
contain-intrinsic-sizeuse theautokeyword 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 asrem,lh,cap, orchthat match values used for the elements within the contents rather thanpx. If the content for items in a group is not consistently sized, then use an average size.Use
contain: layout style paintto 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
contentto 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
contentto 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";, butcontent: "🎉" / "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