experiments in a post-browser web
10
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat(components): add glass-morphism tokens and component modes

Add reusable glass/transparency design tokens so any extension can use
OS-like transparent overlays:

- tokens.css: Add --peek-glass-* tokens (blur, bg, border, shadow, text)
- theme.js: Add light/dark glass token variants
- peek-card: Add `glass` boolean property for glass-morphism styling
- peek-grid: Add `overlay` boolean property for overlay-optimized spacing
- windows.css: Refactor to use glass tokens (preserves existing appearance)

Extensions can now use `<peek-card glass interactive>` for transparent
blurred cards that work across themes.

+128 -24
+47 -1
app/components/peek-card.js
··· 10 10 * @prop {boolean} selected - Visual selected state 11 11 * @prop {boolean} elevated - Add elevation shadow 12 12 * @prop {boolean} bordered - Show border (default true) 13 + * @prop {boolean} glass - Apply glass-morphism effect (transparent blur) 13 14 * 14 15 * @slot - Default slot for card body content 15 16 * @slot header - Card header content ··· 53 54 interactive: { type: Boolean, reflect: true }, 54 55 selected: { type: Boolean, reflect: true }, 55 56 elevated: { type: Boolean, reflect: true }, 56 - bordered: { type: Boolean, reflect: true } 57 + bordered: { type: Boolean, reflect: true }, 58 + glass: { type: Boolean, reflect: true } 57 59 }; 58 60 59 61 static styles = [ ··· 119 121 box-shadow: var(--peek-focus-ring); 120 122 } 121 123 124 + /* Glass-morphism */ 125 + :host([glass]) .card { 126 + background: var(--peek-glass-bg); 127 + backdrop-filter: blur(var(--peek-glass-blur)); 128 + -webkit-backdrop-filter: blur(var(--peek-glass-blur)); 129 + border-color: var(--peek-glass-border); 130 + color: var(--peek-glass-text); 131 + } 132 + 133 + :host([glass]) .header { 134 + color: var(--peek-glass-text); 135 + } 136 + 137 + :host([glass]) .body { 138 + color: var(--peek-glass-text-secondary); 139 + } 140 + 141 + :host([glass]) .footer { 142 + color: var(--peek-glass-text-muted); 143 + border-top-color: var(--peek-glass-border); 144 + } 145 + 146 + :host([glass][interactive]) .card:hover { 147 + background: var(--peek-glass-bg-hover); 148 + border-color: var(--peek-glass-border-hover); 149 + transform: translateY(-2px); 150 + box-shadow: var(--peek-glass-shadow); 151 + } 152 + 153 + :host([glass][selected]) .card { 154 + background: var(--peek-glass-bg-active); 155 + border-color: var(--theme-accent, #007aff); 156 + box-shadow: 0 0 0 2px var(--theme-accent, #007aff); 157 + } 158 + 159 + :host([glass][selected]) .card:hover { 160 + background: rgba(255, 255, 255, 0.25); 161 + } 162 + 163 + :host([glass][interactive]) .card:active { 164 + transform: scale(0.99); 165 + } 166 + 122 167 /* Sections */ 123 168 .header { 124 169 padding: var(--peek-card-padding, var(--peek-space-lg)); ··· 198 243 this.selected = false; 199 244 this.elevated = false; 200 245 this.bordered = true; 246 + this.glass = false; 201 247 } 202 248 203 249 render() {
+14 -1
app/components/peek-grid.js
··· 11 11 * @prop {number} columns - Fixed column count (overrides auto-fit if set) 12 12 * @prop {string} align - Item alignment: 'start' | 'center' | 'end' | 'stretch' 13 13 * @prop {boolean} dense - Enable dense packing algorithm 14 + * @prop {boolean} overlay - Optimize spacing for overlay contexts 14 15 * 15 16 * @slot - Default slot for grid items 16 17 * ··· 37 38 gap: { type: Number }, 38 39 columns: { type: Number }, 39 40 align: { type: String, reflect: true }, 40 - dense: { type: Boolean } 41 + dense: { type: Boolean }, 42 + overlay: { type: Boolean, reflect: true } 41 43 }; 42 44 43 45 static styles = [ ··· 58 60 grid-auto-flow: dense; 59 61 } 60 62 63 + /* Overlay mode - optimized for transparent overlays */ 64 + :host([overlay]) { 65 + padding: var(--peek-space-2xl, 32px); 66 + } 67 + 68 + :host([overlay]) .grid { 69 + max-width: 1800px; 70 + margin: 0 auto; 71 + } 72 + 61 73 /* Slot styling for items */ 62 74 ::slotted(*) { 63 75 min-width: 0; /* Prevent overflow */ ··· 72 84 this.columns = null; 73 85 this.align = 'stretch'; 74 86 this.dense = false; 87 + this.overlay = false; 75 88 } 76 89 77 90 _getGridColumns() {
+30 -2
app/components/theme.js
··· 73 73 'peek-btn-height-lg': '44px', 74 74 75 75 // Focus ring 76 - 'peek-focus-ring': '0 0 0 2px rgba(0, 122, 255, 0.3)' 76 + 'peek-focus-ring': '0 0 0 2px rgba(0, 122, 255, 0.3)', 77 + 78 + // Glass-morphism tokens (light mode) 79 + 'peek-glass-blur': '10px', 80 + 'peek-glass-blur-heavy': '20px', 81 + 'peek-glass-bg': 'rgba(255, 255, 255, 0.7)', 82 + 'peek-glass-bg-hover': 'rgba(255, 255, 255, 0.8)', 83 + 'peek-glass-bg-active': 'rgba(255, 255, 255, 0.9)', 84 + 'peek-glass-border': 'rgba(0, 0, 0, 0.1)', 85 + 'peek-glass-border-hover': 'rgba(0, 0, 0, 0.15)', 86 + 'peek-glass-shadow': '0 8px 24px rgba(0, 0, 0, 0.15)', 87 + 'peek-glass-text': '#333333', 88 + 'peek-glass-text-secondary': 'rgba(0, 0, 0, 0.6)', 89 + 'peek-glass-text-muted': 'rgba(0, 0, 0, 0.4)', 90 + 'peek-overlay-bg': 'rgba(255, 255, 255, 0.85)', 91 + 'peek-overlay-bg-light': 'rgba(255, 255, 255, 0.85)' 77 92 }; 78 93 79 94 // Built-in dark theme ··· 87 102 'theme-border': '#3a3a3a', 88 103 'peek-shadow-sm': '0 1px 2px rgba(0, 0, 0, 0.3)', 89 104 'peek-shadow-md': '0 2px 4px rgba(0, 0, 0, 0.4)', 90 - 'peek-shadow-lg': '0 4px 12px rgba(0, 0, 0, 0.5)' 105 + 'peek-shadow-lg': '0 4px 12px rgba(0, 0, 0, 0.5)', 106 + 107 + // Glass-morphism tokens (dark mode) 108 + 'peek-glass-bg': 'rgba(255, 255, 255, 0.1)', 109 + 'peek-glass-bg-hover': 'rgba(255, 255, 255, 0.15)', 110 + 'peek-glass-bg-active': 'rgba(255, 255, 255, 0.2)', 111 + 'peek-glass-border': 'rgba(255, 255, 255, 0.1)', 112 + 'peek-glass-border-hover': 'rgba(255, 255, 255, 0.2)', 113 + 'peek-glass-shadow': '0 8px 24px rgba(0, 0, 0, 0.3)', 114 + 'peek-glass-text': 'white', 115 + 'peek-glass-text-secondary': 'rgba(255, 255, 255, 0.6)', 116 + 'peek-glass-text-muted': 'rgba(255, 255, 255, 0.4)', 117 + 'peek-overlay-bg': 'rgba(0, 0, 0, 0.85)', 118 + 'peek-overlay-bg-light': 'rgba(255, 255, 255, 0.85)' 91 119 }; 92 120 93 121 // Theme registry
+17
app/components/tokens.css
··· 78 78 --peek-list-item-padding-x: var(--peek-space-md); 79 79 --peek-list-item-padding-y: var(--peek-space-sm); 80 80 --peek-list-item-gap: var(--peek-space-xs); 81 + 82 + /* Glass-morphism tokens (dark overlay) */ 83 + --peek-glass-blur: 10px; 84 + --peek-glass-blur-heavy: 20px; 85 + --peek-glass-bg: rgba(255, 255, 255, 0.1); 86 + --peek-glass-bg-hover: rgba(255, 255, 255, 0.15); 87 + --peek-glass-bg-active: rgba(255, 255, 255, 0.2); 88 + --peek-glass-border: rgba(255, 255, 255, 0.1); 89 + --peek-glass-border-hover: rgba(255, 255, 255, 0.2); 90 + --peek-glass-shadow: 0 8px 24px rgba(0, 0, 0, 0.3); 91 + --peek-glass-text: white; 92 + --peek-glass-text-secondary: rgba(255, 255, 255, 0.6); 93 + --peek-glass-text-muted: rgba(255, 255, 255, 0.4); 94 + 95 + /* Overlay backgrounds */ 96 + --peek-overlay-bg: rgba(0, 0, 0, 0.85); 97 + --peek-overlay-bg-light: rgba(255, 255, 255, 0.85); 81 98 }
+20 -20
extensions/windows/windows.css
··· 16 16 17 17 body { 18 18 /* Transparent background with slight dark overlay */ 19 - background: rgba(0, 0, 0, 0.85); 19 + background: var(--peek-overlay-bg, rgba(0, 0, 0, 0.85)); 20 20 color: var(--base05); 21 21 min-height: 100vh; 22 22 padding: 48px; ··· 33 33 padding: 16px 24px; 34 34 font-size: 18px; 35 35 font-family: var(--theme-font-sans); 36 - background: rgba(255, 255, 255, 0.1); 37 - border: 1px solid rgba(255, 255, 255, 0.2); 36 + background: var(--peek-glass-bg, rgba(255, 255, 255, 0.1)); 37 + border: 1px solid var(--peek-glass-border-hover, rgba(255, 255, 255, 0.2)); 38 38 border-radius: 12px; 39 - color: white; 39 + color: var(--peek-glass-text, white); 40 40 outline: none; 41 41 transition: all 0.15s ease; 42 - backdrop-filter: blur(10px); 42 + backdrop-filter: blur(var(--peek-glass-blur, 10px)); 43 43 } 44 44 45 45 .search-input:focus { 46 46 border-color: var(--base0D); 47 - background: rgba(255, 255, 255, 0.15); 47 + background: var(--peek-glass-bg-hover, rgba(255, 255, 255, 0.15)); 48 48 } 49 49 50 50 .search-input::placeholder { 51 - color: rgba(255, 255, 255, 0.5); 51 + color: var(--peek-glass-text-secondary, rgba(255, 255, 255, 0.5)); 52 52 } 53 53 54 54 /* Cards container */ ··· 62 62 63 63 /* Card base */ 64 64 .card { 65 - background: rgba(255, 255, 255, 0.1); 65 + background: var(--peek-glass-bg, rgba(255, 255, 255, 0.1)); 66 66 border-radius: 12px; 67 67 padding: 20px; 68 68 cursor: pointer; ··· 70 70 display: flex; 71 71 align-items: flex-start; 72 72 gap: 16px; 73 - backdrop-filter: blur(10px); 74 - border: 1px solid rgba(255, 255, 255, 0.1); 73 + backdrop-filter: blur(var(--peek-glass-blur, 10px)); 74 + border: 1px solid var(--peek-glass-border, rgba(255, 255, 255, 0.1)); 75 75 } 76 76 77 77 .card:hover { 78 - background: rgba(255, 255, 255, 0.15); 78 + background: var(--peek-glass-bg-hover, rgba(255, 255, 255, 0.15)); 79 79 transform: translateY(-2px); 80 - box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3); 80 + box-shadow: var(--peek-glass-shadow, 0 8px 24px rgba(0, 0, 0, 0.3)); 81 81 } 82 82 83 83 .card.selected { 84 - background: rgba(255, 255, 255, 0.2); 84 + background: var(--peek-glass-bg-active, rgba(255, 255, 255, 0.2)); 85 85 border-color: var(--base0D); 86 86 box-shadow: 0 0 0 2px var(--base0D); 87 87 } ··· 96 96 height: 48px; 97 97 border-radius: 8px; 98 98 flex-shrink: 0; 99 - background: rgba(255, 255, 255, 0.1); 99 + background: var(--peek-glass-bg, rgba(255, 255, 255, 0.1)); 100 100 object-fit: contain; 101 101 } 102 102 ··· 109 109 .card-title { 110 110 font-size: 16px; 111 111 font-weight: 600; 112 - color: white; 112 + color: var(--peek-glass-text, white); 113 113 margin-bottom: 4px; 114 114 white-space: nowrap; 115 115 overflow: hidden; ··· 118 118 119 119 .card-url { 120 120 font-size: 13px; 121 - color: rgba(255, 255, 255, 0.6); 121 + color: var(--peek-glass-text-secondary, rgba(255, 255, 255, 0.6)); 122 122 white-space: nowrap; 123 123 overflow: hidden; 124 124 text-overflow: ellipsis; ··· 127 127 128 128 .card-meta { 129 129 font-size: 12px; 130 - color: rgba(255, 255, 255, 0.4); 130 + color: var(--peek-glass-text-muted, rgba(255, 255, 255, 0.4)); 131 131 } 132 132 133 133 /* Empty state */ ··· 135 135 grid-column: 1 / -1; 136 136 text-align: center; 137 137 padding: 80px 24px; 138 - color: rgba(255, 255, 255, 0.5); 138 + color: var(--peek-glass-text-secondary, rgba(255, 255, 255, 0.5)); 139 139 font-size: 18px; 140 140 } 141 141 ··· 146 146 left: 50%; 147 147 transform: translateX(-50%); 148 148 font-size: 13px; 149 - color: rgba(255, 255, 255, 0.4); 149 + color: var(--peek-glass-text-muted, rgba(255, 255, 255, 0.4)); 150 150 text-align: center; 151 151 } 152 152 153 153 .hint kbd { 154 - background: rgba(255, 255, 255, 0.1); 154 + background: var(--peek-glass-bg, rgba(255, 255, 255, 0.1)); 155 155 padding: 2px 8px; 156 156 border-radius: 4px; 157 157 margin: 0 4px;