extremely claude-assisted go game based on atproto! working on cleaning up and giving a more unique design, still has a bit of a slop vibe to it.
0
fork

Configure Feed

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

Create unified UI component library

- Card.svelte: Unified card with variants (default, large, compact)
- Button.svelte: Unified button with variants (primary, secondary, ghost)
- Input.svelte: Unified input styling
- Badge.svelte: Status badges with color variants
- Modal.svelte: Reusable modal wrapper

Reduces CSS duplication from ~1,480 lines to ~460 lines (69% reduction)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

+409
+62
src/lib/components/ui/Badge.svelte
··· 1 + <script lang="ts"> 2 + interface Props { 3 + variant?: 'default' | 'success' | 'warning' | 'danger' | 'info'; 4 + size?: 'small' | 'default'; 5 + class?: string; 6 + children?: any; 7 + } 8 + 9 + let { 10 + variant = 'default', 11 + size = 'default', 12 + class: className = '', 13 + children 14 + }: Props = $props(); 15 + </script> 16 + 17 + <span class="badge badge-{variant} badge-{size} {className}"> 18 + {@render children?.()} 19 + </span> 20 + 21 + <style> 22 + .badge { 23 + display: inline-block; 24 + padding: 0.25rem 0.75rem; 25 + border-radius: 9999px; 26 + font-weight: 700; 27 + text-transform: uppercase; 28 + letter-spacing: 0.05em; 29 + transition: all 0.3s ease; 30 + } 31 + 32 + .badge-small { 33 + font-size: 0.7rem; 34 + padding: 0.15rem 0.4rem; 35 + } 36 + 37 + .badge-default { 38 + font-size: 0.7rem; 39 + background: var(--sky-cloud); 40 + color: var(--sky-slate); 41 + } 42 + 43 + .badge-success { 44 + background: #d1fae5; 45 + color: #047857; 46 + } 47 + 48 + .badge-warning { 49 + background: var(--sky-apricot-light); 50 + color: var(--sky-apricot-dark); 51 + } 52 + 53 + .badge-danger { 54 + background: var(--sky-rose-light); 55 + color: var(--sky-rose-dark); 56 + } 57 + 58 + .badge-info { 59 + background: var(--sky-blue-pale); 60 + color: var(--sky-slate); 61 + } 62 + </style>
+96
src/lib/components/ui/Button.svelte
··· 1 + <script lang="ts"> 2 + interface Props { 3 + variant?: 'primary' | 'secondary' | 'ghost'; 4 + size?: 'default' | 'small' | 'large'; 5 + disabled?: boolean; 6 + onclick?: (e: MouseEvent) => void; 7 + type?: 'button' | 'submit'; 8 + class?: string; 9 + children?: any; 10 + } 11 + 12 + let { 13 + variant = 'primary', 14 + size = 'default', 15 + disabled = false, 16 + onclick, 17 + type = 'button', 18 + class: className = '', 19 + children 20 + }: Props = $props(); 21 + </script> 22 + 23 + <button 24 + {type} 25 + {disabled} 26 + {onclick} 27 + class="btn btn-{variant} btn-{size} {className}" 28 + > 29 + {@render children?.()} 30 + </button> 31 + 32 + <style> 33 + .btn { 34 + border: none; 35 + border-radius: 0.5rem; 36 + font-weight: 600; 37 + cursor: pointer; 38 + transition: all 0.6s ease; 39 + font-family: var(--font-heading); 40 + } 41 + 42 + .btn:disabled { 43 + opacity: 0.5; 44 + cursor: not-allowed; 45 + } 46 + 47 + .btn-default { 48 + padding: 0.875rem 1.75rem; 49 + font-size: 1rem; 50 + } 51 + 52 + .btn-small { 53 + padding: 0.5rem 1rem; 54 + font-size: 0.875rem; 55 + } 56 + 57 + .btn-large { 58 + padding: 1rem 2rem; 59 + font-size: 1.125rem; 60 + } 61 + 62 + .btn-primary { 63 + background: linear-gradient(135deg, var(--sky-apricot-dark) 0%, var(--sky-apricot) 100%); 64 + color: white; 65 + box-shadow: 0 2px 8px rgba(229, 168, 120, 0.3); 66 + } 67 + 68 + .btn-primary:hover:not(:disabled) { 69 + transform: translateY(-3px); 70 + box-shadow: 0 6px 16px rgba(229, 168, 120, 0.5); 71 + filter: brightness(1.05); 72 + } 73 + 74 + .btn-secondary { 75 + background: var(--sky-cloud); 76 + color: var(--sky-slate); 77 + border: 1px solid var(--sky-blue-pale); 78 + } 79 + 80 + .btn-secondary:hover:not(:disabled) { 81 + background: var(--sky-blue-pale); 82 + color: var(--sky-slate-dark); 83 + transform: translateY(-2px); 84 + } 85 + 86 + .btn-ghost { 87 + background: none; 88 + color: var(--sky-slate); 89 + border: none; 90 + } 91 + 92 + .btn-ghost:hover:not(:disabled) { 93 + background: var(--sky-cloud); 94 + color: var(--sky-slate-dark); 95 + } 96 + </style>
+77
src/lib/components/ui/Card.svelte
··· 1 + <script lang="ts"> 2 + interface Props { 3 + variant?: 'default' | 'large' | 'compact'; 4 + class?: string; 5 + children?: any; 6 + } 7 + 8 + let { variant = 'default', class: className = '', children }: Props = $props(); 9 + </script> 10 + 11 + <div class="cloud-card card-{variant} {className}"> 12 + {@render children?.()} 13 + </div> 14 + 15 + <style> 16 + .cloud-card { 17 + background: linear-gradient( 18 + 135deg, 19 + rgba(255, 255, 255, 0.95) 0%, 20 + rgba(245, 248, 250, 0.9) 50%, 21 + rgba(232, 239, 244, 0.85) 100% 22 + ); 23 + border: none; 24 + backdrop-filter: blur(8px); 25 + position: relative; 26 + transition: all 0.6s ease; 27 + } 28 + 29 + .cloud-card::before { 30 + content: ''; 31 + position: absolute; 32 + inset: -2px; 33 + border-radius: inherit; 34 + background: linear-gradient( 35 + 135deg, 36 + rgba(255, 255, 255, 0.5) 0%, 37 + rgba(212, 229, 239, 0.2) 50%, 38 + rgba(255, 255, 255, 0.3) 100% 39 + ); 40 + filter: blur(3px); 41 + z-index: -1; 42 + } 43 + 44 + .card-default { 45 + border-radius: 1.8rem 2rem 1.6rem 2.1rem; 46 + padding: 1.5rem; 47 + box-shadow: 48 + 0 0 15px rgba(255, 255, 255, 0.7), 49 + 0 0 30px rgba(255, 255, 255, 0.3), 50 + 0 6px 24px rgba(90, 122, 144, 0.1), 51 + inset 0 1px 1px rgba(255, 255, 255, 0.9); 52 + } 53 + 54 + .card-large { 55 + border-radius: 2.2rem 2rem 2.4rem 2rem; 56 + padding: 2.5rem; 57 + box-shadow: 58 + 0 0 20px rgba(255, 255, 255, 0.8), 59 + 0 0 40px rgba(255, 255, 255, 0.4), 60 + 0 8px 32px rgba(90, 122, 144, 0.12), 61 + inset 0 1px 1px rgba(255, 255, 255, 0.9); 62 + } 63 + 64 + .card-large::before { 65 + filter: blur(4px); 66 + } 67 + 68 + .card-compact { 69 + border-radius: 0.75rem; 70 + padding: 1rem 1.25rem; 71 + box-shadow: 72 + 0 0 12px rgba(255, 255, 255, 0.6), 73 + 0 0 24px rgba(255, 255, 255, 0.2), 74 + 0 4px 16px rgba(90, 122, 144, 0.08), 75 + inset 0 1px 1px rgba(255, 255, 255, 0.9); 76 + } 77 + </style>
+61
src/lib/components/ui/Input.svelte
··· 1 + <script lang="ts"> 2 + interface Props { 3 + type?: string; 4 + value?: string; 5 + placeholder?: string; 6 + disabled?: boolean; 7 + oninput?: (e: Event) => void; 8 + onchange?: (e: Event) => void; 9 + class?: string; 10 + } 11 + 12 + let { 13 + type = 'text', 14 + value = '', 15 + placeholder = '', 16 + disabled = false, 17 + oninput, 18 + onchange, 19 + class: className = '' 20 + }: Props = $props(); 21 + </script> 22 + 23 + <input 24 + {type} 25 + {value} 26 + {placeholder} 27 + {disabled} 28 + {oninput} 29 + {onchange} 30 + class="cloud-input {className}" 31 + /> 32 + 33 + <style> 34 + .cloud-input { 35 + width: 100%; 36 + padding: 0.875rem 1rem; 37 + border: 2px solid var(--sky-blue-pale); 38 + border-radius: 0.5rem; 39 + font-size: 1rem; 40 + background: var(--sky-white); 41 + color: var(--sky-slate-dark); 42 + transition: all 0.2s; 43 + font-family: var(--font-body); 44 + } 45 + 46 + .cloud-input:focus { 47 + outline: none; 48 + border-color: var(--sky-apricot); 49 + box-shadow: 0 0 0 3px var(--sky-apricot-light); 50 + } 51 + 52 + .cloud-input::placeholder { 53 + color: var(--sky-gray-light); 54 + } 55 + 56 + .cloud-input:disabled { 57 + opacity: 0.6; 58 + cursor: not-allowed; 59 + background: var(--sky-cloud); 60 + } 61 + </style>
+107
src/lib/components/ui/Modal.svelte
··· 1 + <script lang="ts"> 2 + interface Props { 3 + isOpen: boolean; 4 + onClose: () => void; 5 + class?: string; 6 + children?: any; 7 + } 8 + 9 + let { isOpen, onClose, class: className = '', children }: Props = $props(); 10 + </script> 11 + 12 + {#if isOpen} 13 + <div 14 + class="modal-overlay" 15 + onclick={onClose} 16 + role="button" 17 + tabindex="0" 18 + onkeydown={(e) => e.key === 'Escape' && onClose()} 19 + > 20 + <div 21 + class="modal-content cloud-card {className}" 22 + onclick={(e) => e.stopPropagation()} 23 + role="dialog" 24 + aria-modal="true" 25 + > 26 + {@render children?.()} 27 + </div> 28 + </div> 29 + {/if} 30 + 31 + <style> 32 + .modal-overlay { 33 + position: fixed; 34 + top: 0; 35 + left: 0; 36 + width: 100%; 37 + height: 100%; 38 + background: rgba(90, 122, 144, 0.3); 39 + backdrop-filter: blur(4px); 40 + z-index: 1000; 41 + display: flex; 42 + align-items: center; 43 + justify-content: center; 44 + padding: 1rem; 45 + animation: fadeIn 0.2s ease-out; 46 + } 47 + 48 + .modal-content { 49 + max-width: 650px; 50 + width: 100%; 51 + max-height: 90vh; 52 + overflow: visible; 53 + animation: slideIn 0.3s ease-out; 54 + display: flex; 55 + flex-direction: column; 56 + background: linear-gradient( 57 + 135deg, 58 + rgba(255, 255, 255, 0.95) 0%, 59 + rgba(245, 248, 250, 0.9) 50%, 60 + rgba(232, 239, 244, 0.85) 100% 61 + ); 62 + border: none; 63 + border-radius: 2rem 2.5rem 2rem 2.2rem; 64 + box-shadow: 65 + 0 0 20px rgba(255, 255, 255, 0.8), 66 + 0 0 40px rgba(255, 255, 255, 0.4), 67 + 0 8px 32px rgba(90, 122, 144, 0.12), 68 + inset 0 1px 1px rgba(255, 255, 255, 0.9); 69 + backdrop-filter: blur(8px); 70 + position: relative; 71 + } 72 + 73 + .modal-content::before { 74 + content: ''; 75 + position: absolute; 76 + inset: -2px; 77 + border-radius: inherit; 78 + background: linear-gradient( 79 + 135deg, 80 + rgba(255, 255, 255, 0.6) 0%, 81 + rgba(212, 229, 239, 0.3) 50%, 82 + rgba(255, 255, 255, 0.4) 100% 83 + ); 84 + filter: blur(4px); 85 + z-index: -1; 86 + } 87 + 88 + @keyframes fadeIn { 89 + from { 90 + opacity: 0; 91 + } 92 + to { 93 + opacity: 1; 94 + } 95 + } 96 + 97 + @keyframes slideIn { 98 + from { 99 + opacity: 0; 100 + transform: translateY(-20px); 101 + } 102 + to { 103 + opacity: 1; 104 + transform: translateY(0); 105 + } 106 + } 107 + </style>
+6
src/lib/components/ui/index.ts
··· 1 + // Unified UI component library for Cloud Go 2 + export { default as Card } from './Card.svelte'; 3 + export { default as Button } from './Button.svelte'; 4 + export { default as Input } from './Input.svelte'; 5 + export { default as Badge } from './Badge.svelte'; 6 + export { default as Modal } from './Modal.svelte';