Skip to main content
Glama
guidance.ts30.5 kB
/** * Interaction Guidance - Implementation guidance for interactive properties */ import { ImplementationGuidance } from '../../types.js'; export const INTERACTION_GUIDANCE: Record<string, ImplementationGuidance> = { 'scroll-snap-type': { basic_usage: '.container { scroll-snap-type: x mandatory; }', best_practices: [ 'Use mandatory for strict snapping behavior', 'Use proximity for softer snapping that responds to user intent', 'Choose appropriate axis (x, y, both) based on scroll direction', 'Combine with scroll-padding for better alignment' ], fallbacks: [ 'Use JavaScript-based carousel for older browsers', 'Provide alternative navigation methods', 'Use CSS-only solutions with radio buttons' ], example_code: ` /* Horizontal carousel with mandatory snapping */ .carousel { display: flex; overflow-x: auto; scroll-snap-type: x mandatory; scroll-behavior: smooth; gap: 1rem; } /* Vertical scrolling with proximity snapping */ .vertical-scroller { height: 400px; overflow-y: auto; scroll-snap-type: y proximity; } /* Both axes snapping for grid layouts */ .grid-scroller { display: grid; grid-template-columns: repeat(3, 100%); grid-template-rows: repeat(3, 100%); overflow: auto; scroll-snap-type: both mandatory; } /* Photo gallery carousel */ .photo-carousel { display: flex; overflow-x: auto; scroll-snap-type: x mandatory; scroll-padding: 20px; -webkit-overflow-scrolling: touch; } /* Card stack with vertical snapping */ .card-stack { height: 500px; overflow-y: auto; scroll-snap-type: y proximity; scroll-padding-top: 2rem; } /* Responsive scroll snap */ @media (max-width: 768px) { .carousel { scroll-snap-type: x mandatory; scroll-padding: 1rem; } } /* Fallback for unsupported browsers */ @supports not (scroll-snap-type: x mandatory) { .carousel { /* JavaScript carousel implementation required */ } }` }, 'scroll-snap-align': { basic_usage: '.item { scroll-snap-align: center; }', best_practices: [ 'Use start for left/top alignment in carousels', 'Use center for featured content alignment', 'Use end for right/bottom alignment', 'Combine with scroll-margin for fine-tuning' ], fallbacks: [ 'Use CSS transforms for centering', 'JavaScript scroll calculations', 'Fixed positioning alternatives' ], example_code: ` /* Center-aligned carousel items */ .carousel-item { flex: 0 0 80%; scroll-snap-align: center; scroll-margin: 0 2rem; } /* Start-aligned items for consistent left alignment */ .list-item { scroll-snap-align: start; scroll-margin-top: 1rem; } /* End-aligned items for right-side snapping */ .end-aligned { scroll-snap-align: end; scroll-margin-right: 20px; } /* Full-width slides with center alignment */ .slide { width: 100%; flex-shrink: 0; scroll-snap-align: center; } /* Card grid with mixed alignment */ .card { scroll-snap-align: start; margin: 1rem; } .featured-card { scroll-snap-align: center; margin: 2rem; } /* Mobile-optimized carousel */ @media (max-width: 768px) { .carousel-item { flex: 0 0 90%; scroll-snap-align: start; scroll-margin-left: 1rem; } } /* Image gallery with center alignment */ .gallery-image { width: 300px; height: 200px; object-fit: cover; flex-shrink: 0; scroll-snap-align: center; border-radius: 8px; }` }, 'scroll-snap-stop': { basic_usage: '.item { scroll-snap-stop: always; }', best_practices: [ 'Use always to force stops at every snap point', 'Use normal for default behavior allowing skipping', 'Apply to important items that users should not skip', 'Combine with mandatory scroll-snap-type for strict control' ], fallbacks: [ 'Use JavaScript to control scroll behavior', 'Implement custom scroll event handlers', 'Use intersection observer for scroll control' ], example_code: ` /* Force stop at every carousel slide */ .carousel-slide { scroll-snap-align: center; scroll-snap-stop: always; flex: 0 0 100%; } /* Allow skipping through regular items */ .regular-item { scroll-snap-align: start; scroll-snap-stop: normal; } /* Important content that must be seen */ .important-section { scroll-snap-align: start; scroll-snap-stop: always; min-height: 100vh; } /* Product showcase carousel */ .product-carousel { display: flex; overflow-x: auto; scroll-snap-type: x mandatory; } .product-item { flex: 0 0 300px; scroll-snap-align: center; scroll-snap-stop: always; margin: 0 1rem; } /* Onboarding steps that require stopping */ .onboarding-step { width: 100%; height: 100vh; scroll-snap-align: start; scroll-snap-stop: always; padding: 2rem; } /* News articles with selective stopping */ .article-preview { scroll-snap-align: start; scroll-snap-stop: normal; } .featured-article { scroll-snap-align: center; scroll-snap-stop: always; background: var(--highlight-color); } /* Mobile considerations */ @media (max-width: 768px) { .carousel-slide { flex: 0 0 85%; scroll-snap-stop: always; } }` }, 'scroll-padding': { basic_usage: '.container { scroll-padding: 2rem; }', best_practices: [ 'Use to create breathing room around snap points', 'Apply to scroll containers, not individual items', 'Consider header/navigation heights when setting padding', 'Use logical properties for international layouts' ], fallbacks: [ 'Use margins on snap items', 'JavaScript scroll offset calculations', 'CSS transforms for positioning' ], example_code: ` /* Carousel with padding for better visual alignment */ .carousel-container { display: flex; overflow-x: auto; scroll-snap-type: x mandatory; scroll-padding: 2rem; } /* Vertical scroll with top padding for fixed header */ .main-content { height: 100vh; overflow-y: auto; scroll-snap-type: y proximity; scroll-padding-top: 80px; /* Height of fixed header */ } /* Asymmetric padding for design requirements */ .gallery { overflow-x: auto; scroll-snap-type: x mandatory; scroll-padding: 1rem 3rem 1rem 1rem; } /* Logical scroll padding for RTL support */ .international-carousel { overflow-x: auto; scroll-snap-type: x mandatory; scroll-padding-inline: 2rem; scroll-padding-block: 1rem; } /* Full-screen sections with navigation padding */ .section-scroller { height: 100vh; overflow-y: auto; scroll-snap-type: y mandatory; scroll-padding-top: 4rem; /* Navigation height */ scroll-padding-bottom: 2rem; /* Footer clearance */ } /* Card layout with breathing room */ .card-container { padding: 1rem; overflow-x: auto; scroll-snap-type: x proximity; scroll-padding: 1rem 2rem; } /* Responsive scroll padding */ @media (max-width: 768px) { .carousel-container { scroll-padding: 1rem; } .main-content { scroll-padding-top: 60px; /* Smaller mobile header */ } }` }, 'hover-media-queries': { basic_usage: '@media (hover: hover) { .element:hover { background: blue; } }', best_practices: [ 'Always wrap hover effects in @media (hover: hover) to prevent on touch devices', 'Use @media (pointer: fine) for precise pointer interactions', 'Provide alternative feedback for touch devices', 'Test hover behavior on both desktop and mobile devices', 'Consider using @media (any-hover: hover) for devices with mixed input methods' ], fallbacks: [ 'Use touch events like :active for mobile interactions', 'Provide button states that work without hover', 'Use JavaScript touch/click event handling' ], example_code: ` /* ✅ CORRECT: Hover effects only on devices that support hover */ .button { background: #blue; transition: background-color 0.2s ease; } @media (hover: hover) { .button:hover { background: #darkblue; } } /* Alternative touch feedback */ .button:active { background: #darkblue; transform: scale(0.98); } /* ❌ INCORRECT: Hover effects on all devices including touch */ .button:hover { background: #darkblue; /* Will trigger on touch and cause issues */ } /* Advanced: Fine pointer interactions */ @media (pointer: fine) { .tooltip-trigger:hover .tooltip { opacity: 1; visibility: visible; } } /* Coarse pointer (touch) alternative */ @media (pointer: coarse) { .tooltip-trigger:active .tooltip { opacity: 1; visibility: visible; } } /* Mixed input devices */ @media (any-hover: hover) { .nav-item:hover .submenu { display: block; } } @media (any-hover: none) { .nav-item .submenu-toggle { display: block; /* Show toggle button on touch-only devices */ } } /* Card interactions with proper hover handling */ .card { transition: transform 0.2s ease, box-shadow 0.2s ease; } @media (hover: hover) { .card:hover { transform: translateY(-4px); box-shadow: 0 8px 24px rgba(0,0,0,0.15); } } /* Touch device feedback */ .card:active { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0,0,0,0.1); } /* Complex interaction patterns */ .interactive-element { cursor: pointer; } @media (hover: hover) and (pointer: fine) { .interactive-element:hover { background: var(--hover-bg); } .interactive-element:hover::after { content: "Click for more details"; position: absolute; /* tooltip styles */ } } @media (pointer: coarse) { .interactive-element { min-height: 44px; /* Larger touch target */ padding: 12px; /* More generous padding */ } } /* Accessibility: Respect reduced motion preferences */ @media (prefers-reduced-motion: reduce) { .button, .card, .interactive-element { transition: none; } }` }, 'scroll-margin': { basic_usage: '.item { scroll-margin: 1rem; }', best_practices: [ 'Apply to individual snap items, not containers', 'Use to fine-tune snap positioning', 'Consider visual hierarchy when setting margins', 'Combine with scroll-padding for optimal alignment' ], fallbacks: [ 'Use regular margins with adjusted calculations', 'JavaScript-based positioning', 'CSS transforms for offset positioning' ], example_code: ` /* Individual carousel items with scroll margins */ .carousel-item { flex: 0 0 300px; scroll-snap-align: center; scroll-margin: 0 1rem; } /* List items with top margin for spacing */ .section-item { scroll-snap-align: start; scroll-margin-top: 2rem; min-height: 300px; } /* Cards with all-around margins */ .card { scroll-snap-align: center; scroll-margin: 1rem; border-radius: 8px; background: white; box-shadow: 0 2px 8px rgba(0,0,0,0.1); } /* Feature highlighting with larger margins */ .featured-item { scroll-snap-align: center; scroll-margin: 2rem 3rem; transform: scale(1.05); } /* Logical margins for international support */ .international-item { scroll-snap-align: start; scroll-margin-inline: 1rem; scroll-margin-block: 0.5rem; } /* Image gallery with precise margins */ .gallery-item { scroll-snap-align: center; scroll-margin: 10px 20px; border-radius: 4px; overflow: hidden; } /* Article sections with reading flow margins */ .article-section { scroll-snap-align: start; scroll-margin-top: 3rem; padding: 2rem; } /* Mobile adjustments */ @media (max-width: 768px) { .carousel-item { scroll-margin: 0 0.5rem; } .section-item { scroll-margin-top: 1rem; } }` }, 'css-carousel': { basic_usage: '.carousel { scroll-snap-type: x mandatory; overflow-x: auto; }', best_practices: [ 'Use modern ::scroll-button() for auto-generated navigation buttons', 'Use ::scroll-marker for automatic dot navigation indicators', 'Use :target-current to style the active marker', 'Use :target-before and :target-after (Chrome 142+) for advanced marker styling', 'Combine with scroll-snap-type for smooth scrolling behavior', 'Always provide fallback for older browsers using @supports', 'Test accessibility with keyboard and screen readers', 'Consider prefers-reduced-motion for animations' ], fallbacks: [ 'Use traditional HTML buttons with JavaScript for navigation', 'Use radio button technique for CSS-only carousel', 'Use Intersection Observer API for active state detection', 'Provide manual dot navigation with HTML elements' ], example_code: ` /* ✅ MODERN CSS-ONLY CAROUSEL - No JavaScript needed! */ /* 1. Basic carousel container with scroll snap */ .carousel { display: flex; overflow-x: auto; scroll-snap-type: x mandatory; scroll-behavior: smooth; gap: 1rem; scrollbar-width: none; /* Firefox */ -webkit-overflow-scrolling: touch; } .carousel::-webkit-scrollbar { display: none; /* Chrome, Safari */ } /* 2. Carousel items */ .carousel-item { flex: 0 0 100%; scroll-snap-align: start; scroll-snap-stop: always; } /* 3. ::scroll-button() - Auto-generated navigation buttons */ .carousel::scroll-button(*) { padding: 1rem; background: oklch(0% 0 0 / 0.5); color: oklch(100% 0 0); border: none; border-radius: 50%; cursor: pointer; transition: background 0.2s ease; } .carousel::scroll-button(left), .carousel::scroll-button(inline-start) { content: "⬅" / "Scroll Left"; } .carousel::scroll-button(right), .carousel::scroll-button(inline-end) { content: "⮕" / "Scroll Right"; } .carousel::scroll-button(*):hover { background: oklch(0% 0 0 / 0.7); } .carousel::scroll-button(*):focus-visible { outline: 2px solid oklch(55% 0.25 264); outline-offset: 5px; } /* Auto-disabled when can't scroll further - no JS needed! */ .carousel::scroll-button(*):disabled { opacity: 0.3; cursor: not-allowed; } /* 4. ::scroll-marker - Auto-generated dot navigation */ .carousel::scroll-marker { content: ""; inline-size: 10px; block-size: 10px; border-radius: 50%; background: oklch(0% 0 0 / 0.3); border: 2px solid transparent; cursor: pointer; transition: all 0.2s ease; } .carousel::scroll-marker:hover { background: oklch(0% 0 0 / 0.5); transform: scale(1.1); } .carousel::scroll-marker:focus-visible { outline: 2px solid oklch(55% 0.25 264); outline-offset: 3px; } /* 5. :target-current - Active marker state (no JS needed!) */ .carousel::scroll-marker:target-current { background: oklch(55% 0.25 264); transform: scale(1.2); } /* 6. :target-before and :target-after - NEW in Chrome 142+ */ /* Style markers before the active one */ .carousel::scroll-marker:target-before { opacity: 0.4; background: oklch(85% 0 0); } /* Style markers after the active one */ .carousel::scroll-marker:target-after { opacity: 0.4; background: oklch(85% 0 0); } /* 7. ::scroll-marker-group - Style the markers container */ .carousel::scroll-marker-group { display: flex; gap: 0.5rem; padding: 1rem; justify-content: center; background: oklch(100% 0 0 / 0.8); border-radius: 1rem; } /* Control marker group position */ .carousel { scroll-marker-group: after; /* Options: before, after, none */ } /* 8. Advanced: Custom marker content with attr() and counters */ .carousel-item { counter-increment: slide; } .carousel::scroll-marker { content: counter(slide); display: flex; align-items: center; justify-content: center; inline-size: 2rem; block-size: 2rem; font-size: 0.875rem; font-weight: 600; } /* 9. Vertical carousel variant */ .vertical-carousel { display: flex; flex-direction: column; overflow-y: auto; scroll-snap-type: y mandatory; block-size: 400px; } .vertical-carousel::scroll-button(up), .vertical-carousel::scroll-button(block-start) { content: "⬆" / "Scroll Up"; } .vertical-carousel::scroll-button(down), .vertical-carousel::scroll-button(block-end) { content: "⬇" / "Scroll Down"; } /* 10. Accessibility & Progressive Enhancement */ @supports not (selector(::scroll-button(*))) { /* Fallback for older browsers */ .carousel-nav-fallback { display: flex; gap: 0.5rem; } .carousel-nav-fallback button { padding: 0.5rem 1rem; background: oklch(55% 0.25 264); color: white; border: none; border-radius: 0.25rem; cursor: pointer; } } /* Respect user motion preferences */ @media (prefers-reduced-motion: reduce) { .carousel { scroll-behavior: auto; } .carousel::scroll-marker, .carousel::scroll-button(*) { transition: none; } } /* 11. Responsive adjustments */ @media (max-width: 768px) { .carousel-item { flex: 0 0 90%; } .carousel::scroll-button(*) { padding: 0.75rem; } .carousel::scroll-marker { inline-size: 8px; block-size: 8px; } } /* 12. Real-world example: Photo gallery carousel */ .photo-gallery { display: flex; overflow-x: auto; scroll-snap-type: x mandatory; scroll-behavior: smooth; gap: 1rem; padding: 1rem; scroll-marker-group: after; } .photo-gallery img { flex: 0 0 min(600px, 80vw); scroll-snap-align: center; border-radius: 0.5rem; object-fit: cover; aspect-ratio: 16/9; } .photo-gallery::scroll-button(*) { padding: 1rem; background: oklch(100% 0 0 / 0.9); color: oklch(0% 0 0); border: none; border-radius: 50%; box-shadow: 0 2px 8px oklch(0% 0 0 / 0.2); } .photo-gallery::scroll-marker { content: ""; inline-size: 12px; block-size: 12px; border-radius: 50%; background: oklch(100% 0 0 / 0.5); border: 2px solid oklch(55% 0.25 264); } .photo-gallery::scroll-marker:target-current { background: oklch(55% 0.25 264); transform: scale(1.3); } /* Reference: https://lukaschylik.dev/blog/articles/css-carousels/ * MDN: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_overflow/CSS_carousels * Browser support: Chrome 142+, limited support in other browsers * Always test with @supports for production use */` }, '::scroll-button()': { basic_usage: '.carousel::scroll-button(*) { padding: 1rem; }', best_practices: [ 'Auto-generates navigation buttons for scrollable containers', 'Buttons are automatically disabled when scrolling is not possible', 'Use (*) selector for common styles across all buttons', 'Specify direction: left, right, up, down, inline-start, inline-end, block-start, block-end', 'Buttons have native focus state for accessibility', 'Use content property with accessible labels', 'Always provide hover and focus-visible states' ], fallbacks: [ 'Use traditional HTML buttons with JavaScript', 'Implement custom navigation with click handlers', 'Use @supports to detect and provide fallbacks' ], example_code: ` /* Auto-generated carousel navigation buttons */ .carousel { display: flex; overflow-x: auto; scroll-snap-type: x mandatory; } /* Common styles for all buttons */ .carousel::scroll-button(*) { padding: 1rem; background: oklch(0% 0 0 / 0.5); color: oklch(100% 0 0); border: none; border-radius: 50%; cursor: pointer; transition: background 0.2s ease; } /* Specific buttons with accessible content */ .carousel::scroll-button(left) { content: "⬅" / "Scroll Left"; } .carousel::scroll-button(right) { content: "⮕" / "Scroll Right"; } /* Logical properties for international support */ .carousel::scroll-button(inline-start) { content: "◀" / "Previous"; } .carousel::scroll-button(inline-end) { content: "▶" / "Next"; } /* Hover and focus states */ .carousel::scroll-button(*):hover { background: oklch(0% 0 0 / 0.7); } .carousel::scroll-button(*):focus-visible { outline: 2px solid oklch(55% 0.25 264); outline-offset: 5px; } /* Auto-disabled state - no JavaScript needed! */ .carousel::scroll-button(*):disabled { opacity: 0.3; cursor: not-allowed; } /* Vertical carousel buttons */ .vertical-carousel::scroll-button(up) { content: "⬆" / "Scroll Up"; } .vertical-carousel::scroll-button(down) { content: "⬇" / "Scroll Down"; } /* Custom styling example */ .fancy-carousel::scroll-button(*) { padding: 1.5rem; background: linear-gradient(135deg, oklch(55% 0.25 264), oklch(65% 0.2 300)); color: white; border-radius: 0.5rem; box-shadow: 0 4px 12px oklch(0% 0 0 / 0.2); font-size: 1.5rem; font-weight: bold; } /* Progressive enhancement */ @supports not (selector(::scroll-button(*))) { .carousel-fallback-nav { display: flex; gap: 0.5rem; margin-block-start: 1rem; } } /* Reference: https://lukaschylik.dev/blog/articles/css-carousels/ * MDN: https://developer.mozilla.org/en-US/docs/Web/CSS/::scroll-button */` }, '::scroll-marker': { basic_usage: '.carousel::scroll-marker { content: ""; width: 10px; height: 10px; }', best_practices: [ 'Auto-generates clickable dot indicators for each scroll snap point', 'Use :target-current to style the active marker', 'Use :target-before and :target-after (Chrome 142+) for adjacent markers', 'Always set content property (can be empty string)', 'Markers are automatically clickable and navigate to slides', 'Combine with ::scroll-marker-group to position the container', 'Use counter() for numbered indicators' ], fallbacks: [ 'Use HTML elements with radio buttons for CSS-only solution', 'Implement custom dots with JavaScript and Intersection Observer', 'Use @supports for feature detection' ], example_code: ` /* Auto-generated dot navigation for carousel */ .carousel { display: flex; overflow-x: auto; scroll-snap-type: x mandatory; } /* Basic dot markers */ .carousel::scroll-marker { content: ""; inline-size: 10px; block-size: 10px; border-radius: 50%; background: oklch(0% 0 0 / 0.3); border: 2px solid transparent; cursor: pointer; transition: all 0.2s ease; } /* Hover state */ .carousel::scroll-marker:hover { background: oklch(0% 0 0 / 0.5); transform: scale(1.1); } /* Focus state for keyboard navigation */ .carousel::scroll-marker:focus-visible { outline: 2px solid oklch(55% 0.25 264); outline-offset: 3px; } /* Active marker - automatically tracked! */ .carousel::scroll-marker:target-current { background: oklch(55% 0.25 264); transform: scale(1.2); } /* NEW: Style markers before the active one (Chrome 142+) */ .carousel::scroll-marker:target-before { opacity: 0.4; background: oklch(85% 0 0); } /* NEW: Style markers after the active one (Chrome 142+) */ .carousel::scroll-marker:target-after { opacity: 0.4; background: oklch(85% 0 0); } /* Advanced: Numbered markers with counters */ .numbered-carousel { counter-reset: slide; } .numbered-carousel .slide { counter-increment: slide; } .numbered-carousel::scroll-marker { content: counter(slide); display: flex; align-items: center; justify-content: center; inline-size: 2rem; block-size: 2rem; border-radius: 50%; background: oklch(90% 0 0); color: oklch(30% 0 0); font-size: 0.875rem; font-weight: 600; } .numbered-carousel::scroll-marker:target-current { background: oklch(55% 0.25 264); color: oklch(100% 0 0); font-weight: 700; } /* Custom marker content with attr() */ .slide[data-label] { /* markers can access data attributes */ } .custom-carousel::scroll-marker { content: attr(data-label, counter(slide)); /* fallback to counter if no label */ } /* Styled markers for premium look */ .premium-carousel::scroll-marker { content: ""; inline-size: 40px; block-size: 4px; border-radius: 2px; background: oklch(0% 0 0 / 0.2); transition: all 0.3s ease; } .premium-carousel::scroll-marker:target-current { inline-size: 60px; background: oklch(55% 0.25 264); } /* Vertical progress indicators */ .vertical-carousel::scroll-marker { inline-size: 4px; block-size: 40px; border-radius: 2px; } /* Accessibility considerations */ @media (prefers-reduced-motion: reduce) { .carousel::scroll-marker { transition: none; } } /* Responsive markers */ @media (max-width: 768px) { .carousel::scroll-marker { inline-size: 8px; block-size: 8px; } } /* Progressive enhancement */ @supports not (selector(::scroll-marker)) { .carousel-dots-fallback { display: flex; gap: 0.5rem; justify-content: center; padding: 1rem; } } /* Reference: https://lukaschylik.dev/blog/articles/css-carousels/ * MDN: https://developer.mozilla.org/en-US/docs/Web/CSS/::scroll-marker */` }, '::scroll-marker-group': { basic_usage: '.carousel::scroll-marker-group { display: flex; gap: 0.5rem; }', best_practices: [ 'Styles the container that holds all ::scroll-marker elements', 'Use display: flex or grid for layout control', 'Control position with scroll-marker-group property (before, after, none)', 'Add padding, gap, and background for visual grouping', 'Works like a normal element - accepts all layout properties' ], fallbacks: [ 'Use wrapper div with CSS for older browsers', 'Implement custom container with JavaScript', 'Use @supports for progressive enhancement' ], example_code: ` /* Style the markers container */ .carousel { scroll-marker-group: after; /* Position: before, after, or none */ } .carousel::scroll-marker-group { display: flex; gap: 0.5rem; padding: 1rem; justify-content: center; background: oklch(100% 0 0 / 0.8); border-radius: 1rem; box-shadow: 0 2px 8px oklch(0% 0 0 / 0.1); } /* Position before the carousel */ .carousel-before { scroll-marker-group: before; } .carousel-before::scroll-marker-group { margin-block-end: 1rem; } /* Position after the carousel (default) */ .carousel-after { scroll-marker-group: after; } .carousel-after::scroll-marker-group { margin-block-start: 1rem; } /* Hide marker group */ .carousel-hidden { scroll-marker-group: none; } /* Grid layout for markers */ .grid-carousel::scroll-marker-group { display: grid; grid-template-columns: repeat(auto-fit, minmax(2rem, 1fr)); gap: 0.5rem; padding: 1rem; background: oklch(95% 0 0); } /* Vertical alignment for side placement */ .side-carousel::scroll-marker-group { display: flex; flex-direction: column; gap: 0.5rem; padding: 1rem; position: absolute; inset-inline-end: 1rem; inset-block-start: 50%; transform: translateY(-50%); } /* Glassmorphism effect */ .modern-carousel::scroll-marker-group { display: flex; gap: 0.75rem; padding: 1rem 1.5rem; background: oklch(100% 0 0 / 0.6); backdrop-filter: blur(10px); border-radius: 2rem; border: 1px solid oklch(100% 0 0 / 0.3); } /* Minimal floating indicators */ .minimal-carousel::scroll-marker-group { display: flex; gap: 0.25rem; padding: 0.5rem; background: none; } /* Responsive marker group */ @media (max-width: 768px) { .carousel::scroll-marker-group { padding: 0.5rem; gap: 0.25rem; } } /* Reference: https://lukaschylik.dev/blog/articles/css-carousels/ * MDN: https://developer.mozilla.org/en-US/docs/Web/CSS/::scroll-marker-group */` }, ':target-current': { basic_usage: '.carousel::scroll-marker:target-current { background: blue; }', best_practices: [ 'Automatically applies to the currently visible scroll marker', 'No JavaScript needed for active state tracking', 'Combine with :target-before and :target-after for contextual styling', 'Works with ::scroll-marker pseudo-elements', 'Use for visual feedback of current slide position', 'Apply transitions for smooth state changes' ], fallbacks: [ 'Use JavaScript with Intersection Observer to track active state', 'Implement CSS-only solution with :target and anchor links', 'Use @supports for feature detection' ], example_code: ` /* Active marker styling - auto-tracked by CSS! */ .carousel::scroll-marker { content: ""; inline-size: 10px; block-size: 10px; border-radius: 50%; background: oklch(0% 0 0 / 0.3); transition: all 0.3s ease; } /* Active state - automatically applied! */ .carousel::scroll-marker:target-current { background: oklch(55% 0.25 264); transform: scale(1.3); box-shadow: 0 0 8px oklch(55% 0.25 264 / 0.5); } /* Combine with hover for rich interactions */ .carousel::scroll-marker:not(:target-current):hover { background: oklch(0% 0 0 / 0.5); transform: scale(1.1); } /* NEW: Context-aware styling with :target-before (Chrome 142+) */ .carousel::scroll-marker:target-before { opacity: 0.5; background: oklch(70% 0 0); } /* NEW: Style markers after active (Chrome 142+) */ .carousel::scroll-marker:target-after { opacity: 0.5; background: oklch(70% 0 0); } /* Create gradient effect across markers */ .carousel::scroll-marker:target-before { opacity: 0.3; } .carousel::scroll-marker:target-current { opacity: 1; background: oklch(55% 0.25 264); } .carousel::scroll-marker:target-after { opacity: 0.3; } /* Numbered markers with active state */ .numbered-carousel::scroll-marker { content: counter(slide); display: flex; align-items: center; justify-content: center; inline-size: 2rem; block-size: 2rem; border-radius: 50%; background: oklch(90% 0 0); color: oklch(30% 0 0); font-weight: 500; } .numbered-carousel::scroll-marker:target-current { background: oklch(55% 0.25 264); color: oklch(100% 0 0); font-weight: 700; transform: scale(1.2); } /* Progress bar style markers */ .progress-carousel::scroll-marker { inline-size: 40px; block-size: 4px; border-radius: 2px; background: oklch(0% 0 0 / 0.2); } .progress-carousel::scroll-marker:target-current { inline-size: 60px; background: oklch(55% 0.25 264); } /* Accessibility */ @media (prefers-reduced-motion: reduce) { .carousel::scroll-marker:target-current { transition: none; } } /* High contrast mode support */ @media (prefers-contrast: high) { .carousel::scroll-marker:target-current { outline: 3px solid currentColor; outline-offset: 2px; } } /* Reference: https://lukaschylik.dev/blog/articles/css-carousels/ * Chrome Status: https://cr-status.appspot.com/feature/5120827674722304 * Browser Support: Chrome 142+ (experimental), limited in other browsers */` } }; export function getInteractionGuidance(property: string, needsFallback: boolean = false): ImplementationGuidance | null { const guidance = INTERACTION_GUIDANCE[property]; if (!guidance) return null; return { ...guidance, fallbacks: needsFallback ? guidance.fallbacks : [] }; }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Luko248/css-first'

If you have feedback or need assistance with the MCP directory API, please join our Discord server