(** Theme CSS files for the scrollycode extension. Each theme sets the CSS custom property values defined in {!Scrollycode_css.structural_css} and imports its Google Fonts. *) (** Warm Workshop theme. Cream background, burnt sienna accents, Fraunces + Source Serif 4. *) let warm_css = {|@import url('https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght@0,9..144,300..900;1,9..144,300..900&family=Source+Code+Pro:ital,wght@0,300..900;1,300..900&family=Source+Serif+4:ital,opsz,wght@0,8..60,300..900;1,8..60,300..900&display=swap'); .sc-container { /* Typography */ --sc-font-display: 'Fraunces', serif; --sc-font-body: 'Source Serif 4', Georgia, serif; --sc-font-code: 'Source Code Pro', monospace; /* Colors */ --sc-bg: #f5f0e6; --sc-text: #2c2416; --sc-text-dim: #8a7c6a; --sc-accent: #c25832; --sc-accent-soft: rgba(194, 88, 50, 0.08); --sc-code-bg: #1a1a2e; --sc-code-text: #d4d0c8; --sc-code-gutter: #3a3a52; --sc-border: rgba(44, 36, 22, 0.1); --sc-focus-bg: rgba(194, 88, 50, 0.06); --sc-panel-radius: 12px; /* Syntax highlighting */ --sc-hl-keyword: #f0a6a0; --sc-hl-type: #8ec8e8; --sc-hl-string: #b8d89a; --sc-hl-comment: #6a6a82; --sc-hl-number: #ddb97a; --sc-hl-module: #e8c87a; --sc-hl-operator: #c8a8d8; --sc-hl-punct: #7a7a92; /* Mobile */ --sc-mobile-step-bg: rgba(255,255,255,0.5); } /* Warm hero: centered */ .sc-container .sc-hero { text-align: center; border-bottom: 1px solid var(--sc-border); } .sc-container .sc-hero h1 { font-style: italic; } .sc-container .sc-hero p { margin: 0 auto; } /* Warm code panel: navy shadow */ .sc-container .sc-code-panel { box-shadow: 0 20px 60px rgba(26, 26, 46, 0.3), 0 0 0 1px rgba(255,255,255,0.03) inset; } /* Warm dots: traffic light colors */ .sc-container .sc-dots span:nth-child(1) { background: #ff5f57; } .sc-container .sc-dots span:nth-child(2) { background: #ffbd2e; } .sc-container .sc-dots span:nth-child(3) { background: #28c840; } /* Warm diff */ .sc-container .sc-diff-block { background: #1e1b2e; } .sc-container .sc-diff-added { background: rgba(80, 200, 80, 0.15); border-left: 3px solid #4caf50; } .sc-container .sc-diff-removed { background: rgba(255, 80, 80, 0.12); border-left: 3px solid #ef5350; text-decoration: line-through; opacity: 0.7; } .sc-container .sc-diff-same { opacity: 0.5; } |} (** Dark Terminal theme. Near-black background, phosphor green and amber, JetBrains Mono + Outfit. *) let dark_css = {|@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300..800&family=Outfit:wght@300..900&display=swap'); .sc-container { /* Typography */ --sc-font-display: 'Outfit', sans-serif; --sc-font-body: 'Outfit', sans-serif; --sc-font-code: 'JetBrains Mono', monospace; /* Colors */ --sc-bg: #0a0a0f; --sc-text: #e8e6f0; --sc-text-dim: #6e6b80; --sc-accent: #4ade80; --sc-accent-soft: rgba(74, 222, 128, 0.06); --sc-code-bg: #0f0f18; --sc-code-text: #c8c5d8; --sc-code-gutter: #2a2a3e; --sc-border: rgba(255, 255, 255, 0.06); --sc-focus-bg: rgba(74, 222, 128, 0.04); --sc-panel-radius: 0; /* Syntax highlighting — neon palette */ --sc-hl-keyword: #ff7eb3; --sc-hl-type: #7dd3fc; --sc-hl-string: #4ade80; --sc-hl-comment: #4a4a62; --sc-hl-number: #fbbf24; --sc-hl-module: #c4b5fd; --sc-hl-operator: #67e8f9; --sc-hl-punct: #4a4a62; /* Mobile */ --sc-mobile-step-bg: rgba(255,255,255,0.04); } /* Dark hero: left-aligned, larger */ .sc-container .sc-hero { text-align: left; padding: 8rem 4rem 4rem; max-width: 800px; position: relative; } .sc-container .sc-hero::before { content: ''; position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: radial-gradient(ellipse at 20% 50%, rgba(74, 222, 128, 0.04) 0%, transparent 60%); pointer-events: none; } .sc-container .sc-hero h1 { font-size: clamp(2.8rem, 6vw, 4.5rem); font-weight: 800; letter-spacing: -0.04em; line-height: 1.0; margin-bottom: 1.25rem; } .sc-container .sc-hero h1 em { font-style: normal; color: var(--sc-accent); } .sc-container .sc-hero p { max-width: 50ch; font-weight: 300; } /* Dark layout: narrow prose, wide code */ .sc-container .sc-steps-col { width: 38%; flex: none; padding: 2rem 2.5rem 50vh 4rem; border-right: 1px solid var(--sc-border); } .sc-container .sc-code-col { flex: 1; width: auto; } /* Dark step number: with line */ .sc-container .sc-step-number { font-weight: 700; letter-spacing: 0.15em; display: flex; align-items: center; gap: 0.75rem; } .sc-container .sc-step-number::after { content: ''; flex: 1; height: 1px; background: var(--sc-border); } .sc-container .sc-step h2 { font-size: 1.4rem; line-height: 1.2; } .sc-container .sc-step p { font-size: 0.9rem; max-width: 40ch; font-weight: 300; } /* Dark code panel: full viewport height */ .sc-container .sc-code-panel { top: 0; height: 100vh; margin: 0; border-radius: 0; border-left: 1px solid var(--sc-border); box-shadow: none; } .sc-container .sc-code-header { padding: 1rem 1.5rem; border-bottom: 1px solid var(--sc-border); gap: 1rem; } .sc-container .sc-dots span { width: 8px; height: 8px; background: var(--sc-code-gutter); } .sc-container .sc-filename { color: var(--sc-text-dim); text-align: left; } .sc-container .sc-step-badge { color: var(--sc-accent); background: rgba(74, 222, 128, 0.08); padding: 0.25em 0.75em; border-radius: 3px; } .sc-container .sc-code-body { padding: 1.5rem 0; line-height: 1.75; } .sc-container .sc-line { padding: 0 1.5rem; transition: opacity 0.3s ease, background 0.3s ease; opacity: 0.25; } .sc-container .sc-line.sc-focused { border-left: 2px solid var(--sc-accent); padding-left: calc(1.5rem - 2px); } .sc-container .sc-line-number { margin-right: 2ch; } /* Dark progress: right side, bar style */ .sc-container .sc-progress { left: auto; right: 1.5rem; gap: 10px; } .sc-container .sc-pip { width: 3px; height: 20px; border-radius: 2px; } .sc-container .sc-pip.sc-active { box-shadow: 0 0 12px rgba(74, 222, 128, 0.5); height: 30px; transform: none; } /* Dark mobile */ .sc-container .sc-mobile-step { border-radius: 10px; border: 1px solid rgba(255,255,255,0.08); } .sc-container .sc-mobile-step-num { font-family: var(--sc-font-body); letter-spacing: 0.2em; color: #00d4aa; } .sc-container .sc-mobile-step h2 { color: #e8e6e3; } .sc-container .sc-mobile-step p { color: rgba(232,230,227,0.7); } .sc-container .sc-diff-block { background: #0d1117; border: 1px solid rgba(0,212,170,0.15); } .sc-container .sc-diff-added { background: rgba(0, 212, 170, 0.12); border-left: 3px solid #00d4aa; } .sc-container .sc-diff-removed { background: rgba(255, 80, 80, 0.1); border-left: 3px solid #ff6b6b; text-decoration: line-through; opacity: 0.6; } .sc-container .sc-diff-same { opacity: 0.4; } .sc-container .sc-playground-btn { border-color: rgba(0,212,170,0.3); color: #00d4aa; } .sc-container .sc-playground-btn:hover { background: rgba(0,212,170,0.1); border-color: #00d4aa; } |} (** Notebook theme. Soft white, blue-violet accent, Newsreader + DM Sans. *) let notebook_css = {|@import url('https://fonts.googleapis.com/css2?family=DM+Mono:wght@300;400;500&family=DM+Sans:ital,opsz,wght@0,9..40,300..900;1,9..40,300..900&family=Newsreader:ital,opsz,wght@0,6..72,300..800;1,6..72,300..800&display=swap'); .sc-container { /* Typography */ --sc-font-display: 'Newsreader', serif; --sc-font-body: 'DM Sans', sans-serif; --sc-font-code: 'DM Mono', 'Source Code Pro', monospace; /* Colors */ --sc-bg: #fafbfe; --sc-text: #1a1a2e; --sc-text-dim: #64648a; --sc-accent: #6366f1; --sc-accent-soft: rgba(99, 102, 241, 0.06); --sc-code-bg: #1e1e32; --sc-code-text: #d1d0e0; --sc-code-gutter: #3a3a52; --sc-border: rgba(99, 102, 241, 0.08); --sc-focus-bg: rgba(99, 102, 241, 0.05); --sc-panel-radius: 16px; /* Syntax highlighting — cool tones */ --sc-hl-keyword: #a78bfa; --sc-hl-type: #67e8f9; --sc-hl-string: #86efac; --sc-hl-comment: #4a4a62; --sc-hl-number: #fde68a; --sc-hl-module: #f9a8d4; --sc-hl-operator: #93c5fd; --sc-hl-punct: #4a4a62; /* Mobile */ --sc-mobile-step-bg: #ffffff; } /* Notebook hero: left-aligned, accent underline */ .sc-container .sc-hero { text-align: left; padding: 6rem 0 3rem; max-width: 640px; margin: 0 auto; border-bottom: 2px solid var(--sc-accent); position: relative; } .sc-container .sc-hero::after { content: ''; position: absolute; bottom: -2px; left: 0; width: 120px; height: 2px; background: var(--sc-accent); box-shadow: 0 0 16px rgba(99, 102, 241, 0.4); } .sc-container .sc-hero h1 { font-size: clamp(2rem, 4vw, 2.8rem); font-weight: 600; letter-spacing: -0.02em; line-height: 1.15; } .sc-container .sc-hero p { font-size: 1rem; max-width: 52ch; font-weight: 400; } /* Notebook layout: constrained width */ .sc-container .sc-tutorial { max-width: 1200px; margin: 0 auto; } .sc-container .sc-steps-col { padding: 2rem 3rem 50vh 0; max-width: 420px; } .sc-container .sc-code-col { flex: 1; width: auto; } /* Notebook step: accent line indicator */ .sc-container .sc-step { min-height: 60vh; padding: 1.5rem 0; position: relative; } .sc-container .sc-step::before { content: ''; position: absolute; left: -1.5rem; top: 50%; transform: translateY(-50%); width: 3px; height: 0; background: var(--sc-accent); border-radius: 2px; transition: height 0.4s cubic-bezier(0.22, 1, 0.36, 1); } .sc-container .sc-step-number { font-family: var(--sc-font-body); font-weight: 700; letter-spacing: 0.12em; display: flex; align-items: center; gap: 0.5rem; } .sc-container .sc-step h2 { font-size: 1.3rem; font-weight: 600; letter-spacing: -0.01em; margin-bottom: 0.6rem; line-height: 1.3; } .sc-container .sc-step p { font-size: 0.88rem; max-width: 42ch; } /* Notebook code panel */ .sc-container .sc-code-panel { top: 8vh; height: 84vh; margin: 0 0 0 2rem; box-shadow: 0 24px 80px rgba(30, 30, 50, 0.15), 0 0 0 1px rgba(99, 102, 241, 0.08); } .sc-container .sc-code-header { background: rgba(99, 102, 241, 0.04); border-bottom: 1px solid rgba(255,255,255,0.04); gap: 0.75rem; } .sc-container .sc-dots span { width: 9px; height: 9px; background: rgba(255,255,255,0.08); } .sc-container .sc-code-body { font-size: 0.78rem; line-height: 1.75; } .sc-container .sc-line { opacity: 0.3; } /* Notebook progress: left side, square pips */ .sc-container .sc-progress { left: 2rem; gap: 6px; } .sc-container .sc-pip { width: 8px; height: 8px; border-radius: 3px; } .sc-container .sc-pip.sc-active { box-shadow: 0 0 10px rgba(99, 102, 241, 0.4); border-radius: 2px; width: 8px; height: 16px; transform: none; } /* Notebook mobile */ .sc-container .sc-mobile-step { border-radius: 6px; border: 1px solid #e0ddd8; } .sc-container .sc-mobile-step-num { font-family: var(--sc-font-body); font-weight: 600; color: #0066cc; } .sc-container .sc-mobile-step h2 { color: #1a1a1a; } .sc-container .sc-mobile-step p { color: #4a4a4a; } .sc-container .sc-diff-block { background: #282c34; border: 1px solid #e0ddd8; font-family: 'IBM Plex Mono', monospace; } .sc-container .sc-diff-added { background: rgba(0, 102, 204, 0.12); border-left: 3px solid #0066cc; } .sc-container .sc-diff-removed { background: rgba(220, 50, 50, 0.1); border-left: 3px solid #dc3232; text-decoration: line-through; opacity: 0.6; } .sc-container .sc-diff-same { opacity: 0.4; } .sc-container .sc-playground-btn { border-color: rgba(0,102,204,0.3); color: #0066cc; } .sc-container .sc-playground-btn:hover { background: rgba(0,102,204,0.1); border-color: #0066cc; } |} (** Register all theme CSS files as support files *) let () = let register name content = Odoc_extension_api.Registry.register_support_file ~prefix:"scrolly" { filename = "extensions/scrollycode-" ^ name ^ ".css"; content = Inline content; } in register "warm" warm_css; register "dark" dark_css; register "notebook" notebook_css