/** * Oklume — OKLCH Color Picker by Anton Ipatov * https://twitter.com/ipatovanton * Licensed under MIT (with attribution requirement) * * Production-ready, stable, and responsive color picker */ /* ==================================================================== ISOLATION & RESET ==================================================================== */ /* Reset only for Oklume elements to avoid conflicts with external styles */ .oklume, .oklume *, .oklume *::before, .oklume *::after { box-sizing: border-box; margin: 0; padding: 0; } .oklume { line-height: 1.5; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } /* ==================================================================== CSS VARIABLES ==================================================================== */ :root { /* Colors */ --oklume-bg: oklch(1 0 0); --oklume-border: oklch(0.9 0 0); --oklume-text: oklch(0.25 0 0); --oklume-text-muted: oklch(0.45 0 0); --oklume-accent: oklch(0.5 0.25 250); /* Container dimensions */ --oklume-max-width: min(350px, 100vw - 32px); --oklume-border-width: 1px; /* Preview dimensions */ --oklume-preview-height: clamp(10px, 20vw, 120px); /* Palette dimensions */ --oklume-swatch-size: clamp(28px, 8vw, 36px); --oklume-swatch-radius: clamp(3px, 1vw, 6px); --oklume-palette-max-height: 100%; --oklume-palette-padding: 4px; --oklume-scrollbar-height: 8px; --oklume-scrollbar-radius: 4px; /* Sliders */ --oklume-slider-height: clamp(6px, 2vw, 8px); --oklume-slider-radius: 3px; --oklume-thumb-size: clamp(20px, 5vw, 24px); --oklume-thumb-scale-hover: 1.2; /* Value fields */ --oklume-value-padding-block: clamp(10px, 3vw, 12px); --oklume-value-padding-inline: clamp(12px, 3.5vw, 14px); --oklume-value-radius: clamp(3px, 1vw, 5px); --oklume-value-border-width: 1px; /* Spacing and gaps */ --oklume-gap: clamp(3px, 1vw, 6px); --oklume-gap-small: clamp(6px, 2vw, 8px); --oklume-gap-medium: clamp(8px, 2.5vw, 12px); --oklume-gap-slider: clamp(6px, 2vw, 10px); --oklume-gap-section: clamp(12px, 4vw, 20px); --oklume-gap-large: clamp(16px, 5vw, 24px); --oklume-padding: clamp(8px, 3vw, 16px); /* Border radius */ --oklume-radius: clamp(4px, 1.5vw, 8px); /* Font sizes */ --oklume-font-size-label: clamp(13px, 3.5vw, 15px); --oklume-font-size-value: clamp(12px, 3.5vw, 14px); /* Swatch hover */ --oklume-swatch-scale-hover: 1.15; --oklume-swatch-scale-active: 0.95; /* Animation timing */ --oklume-transition-speed: 0.2s; --oklume-transition-easing: cubic-bezier(0.4, 0, 0.2, 1); } /* ==================================================================== BASE CONTAINER ==================================================================== */ /* Extended mode - full picker interface */ .oklume--expanded { display: block; font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: var(--oklume-bg); border: var(--oklume-border-width) solid var(--oklume-border); border-radius: var(--oklume-radius); padding: var(--oklume-padding); width: 100%; max-width: var(--oklume-max-width); color: var(--oklume-text); overflow: hidden; position: relative; min-width: 0; } /* Compact mode - popup overlay only */ .oklume--compact { font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; color: var(--oklume-text); position: fixed; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 10000; overflow: hidden; } /* ==================================================================== PREVIEW ==================================================================== */ .oklume-preview { width: 100%; margin-bottom: var(--oklume-gap-section); min-width: 0; } .oklume-preview-box { display: block; width: 100%; height: var(--oklume-preview-height); border-radius: var(--oklume-radius); transition: background-color var(--oklume-transition-speed) var(--oklume-transition-easing); box-shadow: 0 2px 8px oklch(0 0 0 / 0.1); will-change: background-color; } /* ==================================================================== CONTENT CONTAINER ==================================================================== */ .oklume--expanded .oklume-content, .oklume--compact .oklume-content { display: flex; flex-direction: column; gap: var(--oklume-gap-section); min-width: 0; } /* ==================================================================== COLOR PALETTE ==================================================================== */ .oklume-palette { display: grid; grid-template-rows: repeat(8, var(--oklume-swatch-size)); grid-auto-flow: column; grid-auto-columns: var(--oklume-swatch-size); gap: var(--oklume-gap); width: 100%; max-height: var(--oklume-palette-max-height); overflow-x: auto; overflow-y: hidden; padding: var(--oklume-palette-padding); min-width: 0; /* Modern scrollbar styling */ scrollbar-width: thin; scrollbar-color: oklch(0.8 0 0) oklch(0.95 0 0); } /* Webkit scrollbar for palette */ .oklume-palette::-webkit-scrollbar { height: var(--oklume-scrollbar-height); } .oklume-palette::-webkit-scrollbar-track { background: oklch(0.95 0 0); border-radius: var(--oklume-scrollbar-radius); } .oklume-palette::-webkit-scrollbar-thumb { background: oklch(0.8 0 0); border-radius: var(--oklume-scrollbar-radius); } @media (hover: hover) { .oklume-palette::-webkit-scrollbar-thumb:hover { background: oklch(0.6 0 0); } } /* Color swatches */ .oklume-swatch { display: block; width: var(--oklume-swatch-size); height: var(--oklume-swatch-size); border: none; box-shadow: 0 2px 8px oklch(0 0 0 / 0.1); border-radius: var(--oklume-swatch-radius); cursor: pointer; transition: all var(--oklume-transition-speed) var(--oklume-transition-easing); background: none; padding: 0; touch-action: manipulation; flex-shrink: 0; will-change: transform; } @media (hover: hover) { .oklume-swatch:hover { transform: scale(var(--oklume-swatch-scale-hover)); box-shadow: 0 4px 12px oklch(0 0 0 / 0.15); z-index: 10; } } .oklume-swatch:active { transform: scale(var(--oklume-swatch-scale-active)); } /* ==================================================================== SLIDERS ==================================================================== */ .oklume-sliders { display: flex; flex-direction: column; gap: var(--oklume-gap-section); min-width: 0; } .oklume-slider { display: flex; flex-direction: column; gap: var(--oklume-gap-slider); min-width: 0; } .oklume-slider label { display: flex; justify-content: space-between; align-items: center; gap: var(--oklume-gap-small); font-size: var(--oklume-font-size-label); color: var(--oklume-text-muted); min-width: 0; } .oklume-slider label strong { color: var(--oklume-text); font-family: ui-monospace, 'SF Mono', Monaco, 'Cascadia Code', 'Courier New', monospace; white-space: nowrap; flex-shrink: 0; } .oklume-slider input[type="range"] { display: block; width: 100%; height: var(--oklume-slider-height); appearance: none; -webkit-appearance: none; border-radius: var(--oklume-slider-radius); outline: none; cursor: pointer; touch-action: manipulation; background: transparent; min-width: 0; } /* Range thumb - Webkit */ .oklume-slider input[type="range"]::-webkit-slider-thumb { appearance: none; -webkit-appearance: none; width: var(--oklume-thumb-size); height: var(--oklume-thumb-size); background: var(--oklume-accent); border-radius: 50%; cursor: pointer; transition: all var(--oklume-transition-speed) var(--oklume-transition-easing); box-shadow: 0 2px 4px oklch(0 0 0 / 0.2); will-change: transform; } /* Range thumb - Firefox */ .oklume-slider input[type="range"]::-moz-range-thumb { width: var(--oklume-thumb-size); height: var(--oklume-thumb-size); background: var(--oklume-accent); border-radius: 50%; cursor: pointer; border: none; transition: all var(--oklume-transition-speed) var(--oklume-transition-easing); box-shadow: 0 2px 4px oklch(0 0 0 / 0.2); will-change: transform; } @media (hover: hover) { .oklume-slider input[type="range"]::-webkit-slider-thumb:hover { transform: scale(var(--oklume-thumb-scale-hover)); } .oklume-slider input[type="range"]::-moz-range-thumb:hover { transform: scale(var(--oklume-thumb-scale-hover)); } } /* ==================================================================== OUTPUT FORMATS ==================================================================== */ .oklume-output { display: flex; flex-direction: column; gap: var(--oklume-gap-medium); min-width: 0; } .oklume-format { display: flex; flex-direction: column; align-items: stretch; gap: var(--oklume-gap-small); min-width: 0; } .oklume-format label { font-size: var(--oklume-font-size-label); font-weight: 600; color: var(--oklume-text); } .oklume-value { background: var(--oklume-bg); display: block; width: 100%; font-family: ui-monospace, 'SF Mono', Monaco, 'Cascadia Code', 'Courier New', monospace; font-size: var(--oklume-font-size-value); color: var(--oklume-text); padding: var(--oklume-value-padding-block) var(--oklume-value-padding-inline); border-radius: var(--oklume-value-radius); border: var(--oklume-value-border-width) solid var(--oklume-border); cursor: text; transition: all var(--oklume-transition-speed) var(--oklume-transition-easing); outline: none; touch-action: manipulation; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; } @media (hover: hover) { .oklume-value:hover { border-color: var(--oklume-accent); } } .oklume-value:focus { border-color: var(--oklume-accent); box-shadow: 0 0 0 3px oklch(0.5 0.25 250 / 0.1); } /* ==================================================================== SAVE BUTTON ==================================================================== */ .oklume-save { display: block; width: 100%; font-family: ui-monospace, 'SF Mono', Monaco, 'Cascadia Code', 'Courier New', monospace; font-size: var(--oklume-font-size-value); color: var(--oklume-text); padding: var(--oklume-value-padding-block) var(--oklume-value-padding-inline); border-radius: var(--oklume-value-radius); border: var(--oklume-value-border-width) solid var(--oklume-border); background: var(--oklume-bg); cursor: pointer; transition: all var(--oklume-transition-speed) var(--oklume-transition-easing); outline: none; touch-action: manipulation; text-align: center; font-weight: 600; min-width: 0; } @media (hover: hover) { .oklume-save:hover { border-color: var(--oklume-accent); background: oklch(0.98 0 0); } } .oklume-save:active { transform: scale(0.98); } .oklume-save:focus-visible { border-color: var(--oklume-accent); box-shadow: 0 0 0 3px oklch(0.5 0.25 250 / 0.1); } /* ==================================================================== COMPACT MODE ==================================================================== */ /* Hide popup by default in compact mode */ .oklume--compact .oklume-popup { display: none; pointer-events: auto; position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: calc(100% - 32px); max-width: var(--oklume-max-width); max-height: calc(100vh - 40px); background: var(--oklume-bg); border: var(--oklume-border-width) solid var(--oklume-border); border-radius: var(--oklume-radius); box-shadow: 0 20px 60px oklch(0 0 0 / 0.3); padding: var(--oklume-padding); overflow-y: auto; overflow-x: hidden; box-sizing: border-box; /* Modern scrollbar styling */ scrollbar-width: thin; scrollbar-color: oklch(0.7 0 0) oklch(0.95 0 0); } /* Show popup when open */ .oklume--compact.oklume--open .oklume-popup { display: flex; flex-direction: column; gap: var(--oklume-gap-section); animation: oklume-popup-fadein 0.2s var(--oklume-transition-easing); } /* Backdrop overlay when compact mode is open */ .oklume--compact.oklume--open::before { content: ''; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: oklch(0 0 0 / 0.5); pointer-events: auto; animation: oklume-backdrop-fadein 0.2s var(--oklume-transition-easing); z-index: -1; } /* Custom scrollbar for compact mode popup - Webkit */ .oklume--compact .oklume-popup::-webkit-scrollbar { width: 8px; } .oklume--compact .oklume-popup::-webkit-scrollbar-track { background: oklch(0.95 0 0); border-radius: 4px; } .oklume--compact .oklume-popup::-webkit-scrollbar-thumb { background: oklch(0.7 0 0); border-radius: 4px; } @media (hover: hover) { .oklume--compact .oklume-popup::-webkit-scrollbar-thumb:hover { background: oklch(0.5 0 0); } } /* ==================================================================== ANIMATIONS ==================================================================== */ @keyframes oklume-popup-fadein { from { opacity: 0; transform: translate(-50%, -50%) scale(0.95); } to { opacity: 1; transform: translate(-50%, -50%) scale(1); } } @keyframes oklume-backdrop-fadein { from { opacity: 0; } to { opacity: 1; } } /* ==================================================================== RESPONSIVE DESIGN ==================================================================== */ /* Mobile adjustments for compact mode */ @media (max-height: 700px) { .oklume--compact .oklume-popup { max-height: calc(100vh - 20px); } } @media (max-width: 480px) { .oklume--compact .oklume-popup { width: calc(100vw - 32px); max-height: calc(100vh - 20px); } } /* ==================================================================== ACCESSIBILITY ==================================================================== */ /* Reduce motion for accessibility */ @media (prefers-reduced-motion: reduce) { .oklume *, .oklume *::before, .oklume *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; } } /* Focus visible for keyboard navigation */ .oklume-swatch:focus-visible, .oklume-slider input[type="range"]:focus-visible, .oklume-value:focus-visible, .oklume-close:focus-visible { outline: 2px solid var(--oklume-accent); outline-offset: 2px; } /* ==================================================================== PRINT STYLES ==================================================================== */ @media print { .oklume--expanded { border: 1px solid #000; box-shadow: none; } .oklume--compact.oklume--open .oklume-popup { position: static; transform: none; box-shadow: none; } .oklume--compact.oklume--open::before { display: none; } }