diff --git a/themes/minimal-black/.gitignore b/themes/minimal-black/.gitignore new file mode 100644 index 0000000..f3c4ca1 --- /dev/null +++ b/themes/minimal-black/.gitignore @@ -0,0 +1,33 @@ +# Hugo +public/ +resources/ +.hugo_build.lock +hugo_stats.json + +# Node.js +node_modules/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* +package-lock.json + +# IDE & Editor +.vscode/ +.idea/ +*.swp +*.swo +*~ +.DS_Store + +# Build artifacts +*.log +*.tmp +*.temp + +# Backup files +*.backup +*.bak + +# OS files +Thumbs.db +Desktop.ini diff --git a/themes/minimal-black/CHANGELOG.md b/themes/minimal-black/CHANGELOG.md new file mode 100644 index 0000000..b80da5c --- /dev/null +++ b/themes/minimal-black/CHANGELOG.md @@ -0,0 +1,111 @@ +# Changelog + +All notable changes to the Minimal Black theme will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Added +- Initial theme release +- Dark and light mode with automatic switching +- True black dark mode with purple accents +- Responsive design for mobile, tablet, and desktop +- Built-in search functionality with keyboard shortcuts (Ctrl/Cmd+K) +- Table of contents for blog posts with active section tracking +- Syntax highlighting with copy button and language labels +- Multiple page layouts: home, blog, projects, about, about-alternative +- Mermaid diagram support +- Gallery shortcode with lightbox +- GitHub-style alerts (note, tip, warning, danger, important) +- Analytics support (Google Analytics, Plausible, Umami, Fathom) +- Font Awesome and Devicon icon support +- Technology stack marquee on home page +- Project portfolio with featured projects +- Blog with tags and categories +- SEO optimized meta tags +- RSS feed generation +- JSON output for search indexing +- Modular CSS architecture +- Configurable stats and skills for about-alternative page +- Favicon and touch icon support +- Social links configuration +- Hero section with customizable badge, title, and CTAs +- "Now" section for quick facts +- Floating action dock +- Mobile-friendly navigation +- Theme toggle button +- Custom 404 page +- Markdown enhancements: + - Enhanced blockquotes with gradient backgrounds + - Improved list styling with hover effects + - Task list support with checkboxes + - Definition lists + - Footnotes + - Table styling with responsive wrapper + - Image captions (optional) + - External link indicators + - Heading anchor links + +### Changed +- N/A (initial release) + +### Deprecated +- N/A (initial release) + +### Removed +- N/A (initial release) + +### Fixed +- N/A (initial release) + +### Security +- N/A (initial release) + +## Release Notes + +### Version 1.0.0 (Unreleased) + +Initial release of Minimal Black theme for Hugo. A minimal, dark-mode first theme designed for developers, designers, and writers who value simplicity and performance. + +**Key Features:** +- ⚡ Fast and lightweight +- 🎨 Beautiful dark mode with true black backgrounds +- 📱 Fully responsive design +- 🔍 Built-in search +- 💻 Excellent code highlighting +- 📚 Comprehensive documentation + +--- + +## Version History Format + +Each version entry should follow this structure: + +```markdown +## [X.Y.Z] - YYYY-MM-DD + +### Added +- New features + +### Changed +- Changes in existing functionality + +### Deprecated +- Soon-to-be removed features + +### Removed +- Removed features + +### Fixed +- Bug fixes + +### Security +- Security patches +``` + +--- + +[Unreleased]: https://gitlab.com/jimchr12/hugo-minimal-black/-/compare/v1.0.0...main +[1.0.0]: https://gitlab.com/jimchr12/hugo-minimal-black/-/releases/v1.0.0 diff --git a/themes/minimal-black/CONTRIBUTING.md b/themes/minimal-black/CONTRIBUTING.md new file mode 100644 index 0000000..60e1ec4 --- /dev/null +++ b/themes/minimal-black/CONTRIBUTING.md @@ -0,0 +1,249 @@ +# Contributing to Minimal Black + +Thank you for your interest in contributing to Minimal Black! This document provides guidelines and instructions for contributing to the theme. + +## Code of Conduct + +By participating in this project, you agree to maintain a respectful and collaborative environment. Be kind, be professional, and be constructive in your feedback. + +## How Can I Contribute? + +### Reporting Bugs + +Before creating a bug report, please check the [existing issues](https://gitlab.com/jimchr12/hugo-minimal-black/-/issues) to avoid duplicates. + +When creating a bug report, include: + +- **Clear title** — Descriptive and specific +- **Steps to reproduce** — Minimal steps needed to reproduce the issue +- **Expected behavior** — What you expected to happen +- **Actual behavior** — What actually happened +- **Screenshots** — If applicable +- **Environment:** + - Hugo version (`hugo version`) + - Operating system + - Browser (if frontend issue) + - Node.js version (`node --version`) + +### Suggesting Enhancements + +Enhancement suggestions are welcome! Please: + +1. **Check existing issues** — Your idea might already be proposed +2. **Provide clear use case** — Explain why this would be useful +3. **Consider alternatives** — Mention any alternative solutions you've considered +4. **Keep it minimal** — The theme prioritizes simplicity + +### Pull Requests + +1. **Fork the repository** and create your branch from `main` +2. **Make your changes** following the code style guidelines below +3. **Test your changes** thoroughly +4. **Update documentation** if you're changing functionality +5. **Commit with clear messages** following the commit conventions +6. **Submit a pull request** with a comprehensive description + +## Development Setup + +### Prerequisites + +- Hugo Extended v0.120.0+ +- Node.js 18+ +- npm or yarn + +### Local Development + +1. **Clone your fork:** + ```bash + git clone https://gitlab.com/YOUR-USERNAME/hugo-minimal-black.git + cd hugo-minimal-black + ``` + +2. **Install dependencies:** + ```bash + npm install + ``` + +3. **Run the example site:** + ```bash + cd exampleSite + hugo server -D --themesDir ../.. + ``` + +4. **Build CSS** (if modifying styles): + ```bash + npx tailwindcss -i ./assets/css/main.css -o ./static/css/main.css + ``` + +### Making Changes + +#### CSS/Styling + +- CSS files are in `assets/css/` +- The theme uses Tailwind CSS with a modular structure +- See [CSS-STRUCTURE.md](CSS-STRUCTURE.md) for architecture details +- Follow the existing pattern: one component per file +- Use CSS custom properties for theme colors +- Ensure changes work in both light and dark modes + +#### HTML/Templates + +- Templates are in `layouts/` +- Follow Hugo's template best practices +- Use semantic HTML5 elements +- Test responsiveness on mobile, tablet, and desktop + +#### JavaScript + +- Keep JavaScript minimal +- Use vanilla JavaScript (no frameworks) +- Ensure compatibility with all modern browsers +- Add comments for complex logic +- Test with JavaScript disabled for core functionality + +### HTML/Templates + +- **2 spaces** for indentation +- **Semantic HTML** — Use appropriate elements +- **Accessibility** — Include ARIA labels where needed +- **Hugo conventions** — Follow Hugo's best practices + +```html +{{ define "main" }} +
+
+

{{ .Title }}

+ {{ .Content }} +
+
+{{ end }} +``` + +### CSS + +- **2 spaces** for indentation +- **BEM-inspired** naming for custom classes +- **Mobile-first** responsive design +- **CSS custom properties** for theme values +- **Comments** for complex styles + +```css +/* Component name */ +.component-name { + property: value; +} + +/* Component variant */ +.component-name--variant { + property: value; +} + +/* Component element */ +.component-name__element { + property: value; +} +``` + +### JavaScript + +- **2 spaces** for indentation +- **camelCase** for variables and functions +- **Descriptive names** — Avoid abbreviations +- **Comments** for non-obvious code +- **Modern syntax** — Use ES6+ features + +```javascript +function handleSearchInput(event) { + const query = event.target.value; + const results = filterPages(query); + renderResults(results); +} +``` + +## Commit Messages + +Follow the [Conventional Commits](https://www.conventionalcommits.org/) specification: + +``` +type(scope): subject + +body (optional) + +footer (optional) +``` + +### Types + +- `feat`: New feature +- `fix`: Bug fix +- `docs`: Documentation changes +- `style`: Code style changes (formatting, etc.) +- `refactor`: Code refactoring +- `perf`: Performance improvements +- `test`: Adding or updating tests +- `chore`: Maintenance tasks + +### Examples + +``` +feat(search): add keyboard navigation to search results + +fix(toc): correct active link highlighting on scroll + +docs(readme): update installation instructions + +style(css): reorganize markdown styles into modules + +refactor(cards): simplify project card component + +perf(images): optimize image loading with lazy loading +``` + +## Testing Checklist + +Before submitting a pull request, ensure: + +- [ ] Hugo builds without errors (`hugo`) +- [ ] No console errors in browser +- [ ] Works in latest Chrome, Firefox, Safari +- [ ] Responsive on mobile, tablet, desktop +- [ ] Dark and light modes both work +- [ ] Accessibility: Keyboard navigation works +- [ ] Performance: Lighthouse score >90 +- [ ] Documentation updated (if applicable) +- [ ] No breaking changes (or clearly documented) + +## File Structure + +``` +minimal-black/ +├── archetypes/ # Content templates +├── assets/ +│ └── css/ # Modular CSS files +├── layouts/ +│ ├── _default/ # Default layouts +│ ├── partials/ # Reusable components +│ └── shortcodes/ # Custom shortcodes +├── static/ +│ └── js/ # JavaScript files +├── exampleSite/ # Example site for testing +├── images/ # Theme screenshots +├── CSS-STRUCTURE.md # CSS architecture docs +├── CONTRIBUTING.md # This file +├── LICENSE # MIT License +├── README.md # Main documentation +└── theme.toml # Theme metadata +``` + +## Questions? + +- **Issues:** [GitLab Issues](https://gitlab.com/jimchr12/hugo-minimal-black/-/issues) +- **Discussions:** [GitLab Discussions](https://gitlab.com/jimchr12/hugo-minimal-black/-/issues) + +## License + +By contributing, you agree that your contributions will be licensed under the MIT License. + +--- + +Thank you for contributing to Minimal Black! 🎉 diff --git a/themes/minimal-black/CSS-STRUCTURE.md b/themes/minimal-black/CSS-STRUCTURE.md new file mode 100644 index 0000000..f840b8f --- /dev/null +++ b/themes/minimal-black/CSS-STRUCTURE.md @@ -0,0 +1,106 @@ +# CSS Architecture + +The Minimal Black theme uses a modular CSS architecture for better maintainability and organization. + +## Directory Structure + +``` +assets/css/ +├── main.css # Entry point (imports all modules) +├── base.css # Tailwind imports & theme variables +├── utilities.css # Utility classes & animations +├── responsive.css # Global responsive styles +├── components/ +│ ├── dock.css # Floating action dock +│ ├── cards.css # Card variants (home, project, post, CTA) +│ ├── navigation.css # Header, footer, nav links +│ ├── search.css # Search overlay & results +│ └── tech-marquee.css # Technology carousel +├── content/ +│ ├── markdown.css # Typography, blockquotes, lists, code +│ └── toc.css # Table of contents sidebar +└── pages/ + ├── about.css # About page with timeline + └── about-alternative.css # Alternative about page layout +``` + +## Module Descriptions + +### Base Layer +- **base.css**: Tailwind directives, CSS custom properties for theming (light/dark mode), and base body styles + +### Utilities +- **utilities.css**: Color utilities, animation classes, and helper classes used throughout the theme + +### Components +Reusable UI components that appear across multiple pages: +- **dock.css**: Floating action button dock with expandable panel +- **cards.css**: All card variants including home cards, project/post cards, CTA cards, and badges +- **navigation.css**: Page layouts, header, footer, navigation links, and theme toggle +- **search.css**: Search modal overlay, search results, empty states, and keyboard hints +- **tech-marquee.css**: Animated technology/skills carousel component + +### Content +Styles specific to content rendering: +- **markdown.css**: Complete markdown styling including typography, blockquotes, lists, code blocks, tables, GitHub-style alerts, and syntax highlighting +- **toc.css**: Table of contents sidebar with sticky positioning and active link tracking + +### Pages +Page-specific styles: +- **about.css**: Standard about page with timeline visualization +- **about-alternative.css**: Alternative about layout with sidebar profile card and stats + +### Responsive +- **responsive.css**: All media queries and mobile optimizations for global components + +## Import Order + +The import order in `main.css` is important: +1. Base styles (Tailwind + variables) +2. Utilities (available to all components) +3. Components (dock → cards → navigation → search → tech) +4. Content styles (markdown → TOC) +5. Page-specific styles +6. Responsive overrides + +## Development Guidelines + +### Adding New Styles +- Component styles → `components/` +- Content/typography → `content/` +- Page-specific → `pages/` +- Utilities → `utilities.css` +- Theme variables → `base.css` + +### Modifying Existing Styles +Each file is focused on a specific concern. Find the relevant module and edit it directly. Hugo will automatically rebuild the combined CSS. + +### Theme Colors +All theme colors are defined as CSS custom properties in `base.css`: +```css +--color-bg /* Background */ +--color-surface /* Card/surface backgrounds */ +--color-text /* Primary text */ +--color-text-muted /* Secondary text */ +--color-border /* Borders */ +--color-accent /* Primary accent color */ +``` + +These variables automatically switch between light and dark themes. + +### Responsive Breakpoints +- Mobile: `max-width: 640px` +- Tablet: `641px - 1023px` +- Desktop: `min-width: 1024px` +- Large Desktop: `min-width: 1536px` +- XL Desktop: `min-width: 1920px` + +## Build Process + +Hugo processes `main.css` through: +1. Resolves `@import` statements +2. Processes Tailwind directives (@tailwind, @apply) +3. Minifies for production +4. Outputs to `public/css/main.css` + +The modular structure is development-only; users receive a single optimized CSS file. diff --git a/themes/minimal-black/LICENSE b/themes/minimal-black/LICENSE new file mode 100644 index 0000000..7a4e610 --- /dev/null +++ b/themes/minimal-black/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Jim Christopoulos + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/themes/minimal-black/README.md b/themes/minimal-black/README.md new file mode 100644 index 0000000..b7a7876 --- /dev/null +++ b/themes/minimal-black/README.md @@ -0,0 +1,457 @@ +# Minimal Black + +A minimal, dark-mode first Hugo theme with true black backgrounds, purple accents, and comprehensive content styling. Perfect for developers, writers and everyone in general who want a clean, fast, and beautiful personal site. + +![Minimal Black Theme](https://gitlab.com/jimchr12/hugo-minimal-black/-/raw/main/images/screenshot.png?ref_type=heads) +![Minimal Black Theme About Page](https://gitlab.com/jimchr12/hugo-minimal-black/-/raw/main/images/about.png?ref_type=heads) +![Minimal Black Theme About Alternative Page](https://gitlab.com/jimchr12/hugo-minimal-black/-/raw/main/images/about-alt.png?ref_type=heads) + +## Features + +- ✨ **Dark & Light Mode** — Auto-switching themes with manual toggle +- 🎨 **True Black Dark Mode** — OLED-friendly with purple (`#a855f7`) accents +- 📱 **Fully Responsive** — Mobile-first design that works on all devices +- 🔍 **Built-in Search** — Fast client-side search with keyboard shortcuts (Ctrl/Cmd+K) +- 📑 **Table of Contents** — Auto-generated TOC for blog posts with active section tracking +- 💻 **Beautiful Code Blocks** — Syntax highlighting with copy button and language labels +- 🎯 **Multiple Layouts** — Home, blog, projects, and two about page variants +- 📊 **Mermaid Diagrams** — Native support for flowcharts and diagrams +- 🖼️ **Image Gallery** — Gallery shortcode with lightbox functionality +- 🏷️ **GitHub-Style Alerts** — Support for note, tip, warning, danger, and important callouts +- 📈 **Analytics Ready** — Built-in support for Google Analytics, Plausible, Umami, and Fathom +- ⚡ **Performance Optimized** — Minimal JavaScript, CSS, and fast load times +- 🎭 **Icon Support** — Font Awesome and Devicon integration + +## Demo + +**Live Demo:** [minimal-black-demo.netlify.app](https://minimal-black-demo.netlify.app) + +## Quick Start + +### Prerequisites + +- Hugo Extended v0.120.0 or higher +- Node.js and npm (for Tailwind CSS compilation) + +### Installation + +1. **Create a new Hugo site** (or use existing): + ```bash + hugo new site my-site + cd my-site + ``` + +2. **Install the theme** using Git submodule: + ```bash + git init + git submodule add https://gitlab.com/jimchr12/hugo-minimal-black.git themes/minimal-black + ``` + + Or clone it directly: + ```bash + git clone https://gitlab.com/jimchr12/hugo-minimal-black.git themes/minimal-black + ``` + +3. **Install Node dependencies** (for Tailwind CSS): + ```bash + cd themes/minimal-black + npm install + cd ../.. + ``` + +4. **Configure your site** — Copy the example configuration: + ```bash + cp themes/minimal-black/exampleSite/hugo.toml ./hugo.toml + ``` + +5. **Start Hugo server**: + ```bash + hugo server -D + ``` + +6. **Visit** `http://localhost:1313` in your browser. + +## Configuration + +### Basic Configuration + +Edit your `hugo.toml` file: + +```toml +baseURL = 'https://yoursite.com/' +languageCode = 'en-us' +title = 'Your Name' +theme = 'minimal-black' + +[params] + brand = "Your Name" + + # Favicon (place files in /static/) + favicon = "favicon.svg" + appleTouchIcon = "apple-touch-icon.png" + + # Theme Configuration + [params.theme] + defaultTheme = "dark" # "light", "dark", or "system" + + # Hero Section + [params.hero] + badge = "Software Engineer" + title = "Hi, I'm Your Name." + role = "Building minimal, fast web experiences." + summary = "Brief description of what you do." + avatar = "images/avatar.jpg" # Optional + location = "City, Country" + + [params.hero.primary] + label = "View projects" + href = "/projects/" + + [params.hero.secondary] + label = "Read the blog" + href = "/blog/" + + # Social Links + [[params.social]] + label = "GitHub" + url = "https://github.com/yourusername" + icon = "fa-brands fa-github" + + [[params.social]] + label = "LinkedIn" + url = "https://linkedin.com/in/yourusername" + icon = "fa-brands fa-linkedin-in" + +[menu] + [[menu.main]] + name = "Home" + url = "/" + weight = 1 + + [[menu.main]] + name = "About" + url = "/about/" + weight = 2 + + [[menu.main]] + name = "Projects" + url = "/projects/" + weight = 3 + + [[menu.main]] + name = "Blog" + url = "/blog/" + weight = 4 + +[markup] + [markup.tableOfContents] + startLevel = 2 + endLevel = 4 + + [markup.goldmark.renderer] + unsafe = true + + [markup.highlight] + codeFences = true + guessSyntax = true + style = "monokai" +``` + +### Advanced Configuration + +#### Analytics + +Google Analytics (GA4): +```toml +[params.analytics] + googleAnalytics = "G-XXXXXXXXXX" +``` + +Plausible Analytics: +```toml +[params.analytics.plausible] + enabled = true + domain = "yourdomain.com" +``` + +Umami Analytics: +```toml +[params.analytics.umami] + enabled = true + scriptUrl = "https://analytics.yourdomain.com/script.js" + websiteId = "your-website-id" +``` + +#### Technology Stack Display + +Show your tech stack on the home page: +```toml +[params.home] + [[params.home.tech]] + label = "Python" + icon = "devicon-python-plain" + + [[params.home.tech]] + label = "Docker" + icon = "devicon-docker-plain" +``` + +#### About Page Customization + +For the alternative about page layout: +```toml +[params.about.alt] + # Stats in profile card + [[params.about.alt.stats]] + value = "5+" + label = "Years Coding" + + [[params.about.alt.stats]] + value = "20+" + label = "Projects" + + # Skills with icons + [[params.about.alt.skills]] + label = "JavaScript" + icon = "devicon-javascript-plain" +``` + +## Content Organization + +### Creating Content + +**Blog Post:** +```bash +hugo new blog/my-first-post.md +``` + +```markdown ++++ +title = "My First Post" +date = "2025-01-01" +author = "Your Name" +tags = ["hugo", "web development"] +categories = ["tutorials"] +draft = false ++++ + +Your content here... +``` + +**Project:** +```bash +hugo new projects/my-project.md +``` + +```markdown ++++ +title = "My Project" +date = "2025-01-01" +description = "Brief project description" +github = "https://github.com/username/project" +demo = "https://project-demo.com" +tags = ["react", "typescript"] +featured = true ++++ + +Project details... +``` + +### About Page + +Create `content/about.md`: + +```markdown ++++ +title = "About Me" +subtitle = "Software Engineer | Open Source Contributor" +layout = "about" # or "about-alternative" ++++ + +Your introduction here. + +--- + +**Job Title** — [Company](https://company.com) +*Dates • Location* + +Description of role... + +--- + +**Another Role** — Company +*Dates • Location* + +Description... +``` + +The `---` separators create timeline entries in the standard about layout, or experience cards in the alternative layout. + +## Shortcodes + +### Gallery + +Create image galleries with lightbox: + +```markdown +{{< gallery >}} +![Alt text](./images/screenshot.png) +![Alt text](./images/about.png) +![Alt text](./images/about-alt.png) +{{< /gallery >}} +``` + +### Alert + +Create GitHub-style callouts: + +```markdown +{{< alert type="note" >}} +This is a note alert. +{{< /alert >}} + +{{< alert type="warning" >}} +This is a warning alert. +{{< /alert >}} +``` + +Available types: `note`, `tip`, `important`, `warning`, `danger` + +### Mermaid Diagrams + +````markdown +```mermaid +graph TD + A[Start] --> B[Process] + B --> C[End] +``` +```` + +## Customization + +### Colors + +Theme colors are defined in `assets/css/base.css`: + +```css +:root { + --color-bg: #f9fafb; /* Light background */ + --color-accent: #a855f7; /* Purple accent */ +} + +html[data-theme="dark"] { + --color-bg: #000000; /* True black */ + --color-accent: #c084fc; /* Lighter purple */ +} +``` + +### CSS Architecture + +The theme uses a modular CSS structure: + +``` +assets/css/ +├── main.css # Imports all modules +├── base.css # Tailwind & variables +├── utilities.css # Utility classes +├── components/ # Reusable components +├── content/ # Content styling +├── pages/ # Page-specific styles +└── responsive.css # Media queries +``` + +See [CSS-STRUCTURE.md](CSS-STRUCTURE.md) for details. + +### Adding Custom CSS + +Create `assets/css/custom.css` in your site root and import it in your config: + +```toml +[params] + customCSS = ["css/custom.css"] +``` + +## Deployment + +### Netlify + +1. Push your site to GitHub +2. Connect to Netlify +3. Build command: `hugo --minify` +4. Publish directory: `public` +5. Environment variables: + ``` + HUGO_VERSION = 0.120.0 + NODE_VERSION = 18 + ``` + +## Browser Support + +- Chrome/Edge (last 2 versions) +- Firefox (last 2 versions) +- Safari (last 2 versions) +- Mobile browsers (iOS Safari, Chrome Mobile) + +## Troubleshooting + +### CSS not loading + +Ensure Tailwind dependencies are installed: +```bash +cd themes/minimal-black +npm install +``` + +Also ensure css is compiled: +```bash +npx tailwindcss -i ./assets/css/main.css -o ./static/css/main.css +``` + +### Search not working + +Check that `[outputs]` includes JSON in `hugo.toml`: +```toml +[outputs] + home = ["HTML", "RSS", "JSON"] +``` + +### Icons not showing + +Verify icon library is enabled: +```toml +[params.icons] + useFontAwesome = true + useDevicon = true +``` + +## Contributing + +Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines. + +1. Fork the repository +2. Create a feature branch +3. Make your changes +4. Submit a pull request + +## License + +This theme is released under the MIT License. See [LICENSE](LICENSE) for details. + +## Credits + +- Built with [Hugo](https://gohugo.io/) +- Styled with [Tailwind CSS](https://tailwindcss.com/) +- Icons from [Font Awesome](https://fontawesome.com/) and [Devicon](https://devicon.dev/) +- Typography plugin by [@tailwindcss/typography](https://github.com/tailwindlabs/tailwindcss-typography) + +## Changelog + +See [CHANGELOG.md](CHANGELOG.md) for version history and updates. + +--- + +**Made with ❤️ by [Jim Christopoulos](https://jimchristopoulos.com/)** + +If you find this theme useful, consider giving it a ⭐ and/or supporting its development! + +[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/jimchr) diff --git a/themes/minimal-black/assets/css/base.css b/themes/minimal-black/assets/css/base.css new file mode 100644 index 0000000..c31d77d --- /dev/null +++ b/themes/minimal-black/assets/css/base.css @@ -0,0 +1,35 @@ +/* ========================================================================== + BASE STYLES + Tailwind imports, CSS variables, theme colors, and base styles + ========================================================================== */ + +@tailwind base; +@tailwind components; +@tailwind utilities; + +/* Theme Variables */ +:root { + /* LIGHT THEME */ + --color-bg: #f9fafb; + --color-surface: #ffffff; + --color-text: #111827; + --color-text-muted: #6b7280; + --color-border: #e5e7eb; + --color-accent: #a855f7; +} + +html[data-theme="dark"] { + /* DARK THEME */ + --color-bg: #000000; /* TRUE BLACK */ + --color-surface: #0a0a0a; + --color-text: #f9fafb; + --color-text-muted: #9ca3af; + --color-border: #27272a; + --color-accent: #c084fc; +} + +/* Base Styles */ +body { + background: var(--color-bg); + color: var(--color-text); +} diff --git a/themes/minimal-black/assets/css/components/cards.css b/themes/minimal-black/assets/css/components/cards.css new file mode 100644 index 0000000..59b34ac --- /dev/null +++ b/themes/minimal-black/assets/css/components/cards.css @@ -0,0 +1,271 @@ +/* ========================================================================== + CARD COMPONENTS + Home cards, project cards, post cards, CTA cards, badges + ========================================================================== */ + + .card-home { + display: flex; + flex-direction: column; + justify-content: space-between; + gap: 0.5rem; + background-color: color-mix(in srgb, var(--color-surface) 96%, transparent); + box-shadow: 0 18px 40px rgba(0, 0, 0, 0.55); + } + + .card-home--project { + position: relative; + overflow: hidden; + transition: + transform 0.18s ease-out, + box-shadow 0.18s ease-out, + border-color 0.18s ease-out, + background-color 0.18s ease-out; + } + + .card-home--project::after { + content: ""; + position: absolute; + inset: -40%; + background: radial-gradient( + circle at 120% 50%, + color-mix(in srgb, var(--color-accent) 70%, transparent), + transparent 60% + ); + opacity: 0; + transform: translateX(-8px); + transition: + opacity 0.22s ease-out, + transform 0.22s ease-out; + pointer-events: none; + } + + .card-home--project:hover { + transform: translateY(-4px) translateX(3px); + border-color: color-mix(in srgb, var(--color-accent) 40%, var(--color-border)); + box-shadow: + 0 18px 40px rgba(0, 0, 0, 0.7), + 16px 0 38px rgba(124, 58, 237, 0.45); /* purple-ish accent on right */ + background-color: color-mix(in srgb, var(--color-surface) 99%, transparent); + } + + .card-home--project:hover::after { + opacity: 0.22; + transform: translateX(0); + } + + .card-home-body { + display: flex; + flex-direction: column; + gap: 0.5rem; + text-decoration: none; + color: inherit; + } + + .card-home--post { + background-color: color-mix(in srgb, var(--color-surface) 94%, transparent); + } + + .card-home-header { + display: flex; + align-items: flex-start; + gap: 0.6rem; + } + + .card-home-icon { + display: inline-flex; + align-items: center; + justify-content: center; + width: 2.1rem; + height: 2.1rem; + border-radius: 999px; + border: 1px solid var(--color-border); + background-color: color-mix(in srgb, var(--color-bg) 92%, transparent); + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4); + font-size: 0.85rem; + } + + .card-badge { + margin-left: 0.25rem; + padding: 0.05rem 0.35rem; + border-radius: 999px; + background-color: color-mix(in srgb, var(--color-accent) 22%, transparent); + color: var(--color-accent); + font-size: 0.6rem; + font-weight: 600; + } + + .card-home-footer { + margin-top: 0.5rem; + display: flex; + align-items: center; + gap: 0.5rem; + justify-content: space-between; + } + + .card-home-footer--buttons { + padding-top: 0.4rem; + border-top: 1px solid color-mix(in srgb, var(--color-border) 80%, transparent); + } + + .card-tag-row { + display: flex; + flex-wrap: wrap; + gap: 0.25rem; + } + + .card-tag-pill { + padding: 0.1rem 0.45rem; + border-radius: 999px; + border: 1px solid color-mix(in srgb, var(--color-border) 80%, transparent); + background-color: color-mix(in srgb, var(--color-bg) 92%, transparent); + font-size: 0.65rem; + color: var(--color-text-muted); + } + + .card-cta { + display: inline-flex; + align-items: center; + gap: 0.35rem; + font-size: 0.7rem; + color: var(--color-text-muted); + text-decoration: none; + padding: 0.25rem 0.6rem; + border-radius: 999px; + border: 1px solid transparent; + transition: + color 0.15s ease-out, + border-color 0.15s ease-out, + background-color 0.15s ease-out, + transform 0.15s ease-out; + } + + .card-cta-btn { + display: inline-flex; + align-items: center; + gap: 0.35rem; + padding: 0.25rem 0.6rem; + border-radius: 8px; + font-size: 0.68rem; + color: var(--color-text-muted); + background-color: color-mix(in srgb, var(--color-bg) 92%, transparent); + border: 1px solid color-mix(in srgb, var(--color-border) 80%, transparent); + text-decoration: none; + transition: + color 0.15s ease-out, + background-color 0.15s ease-out, + border-color 0.15s ease-out, + transform 0.15s ease-out, + box-shadow 0.15s ease-out; + } + + .card-cta-btn:hover { + color: var(--color-accent); + background-color: color-mix(in srgb, var(--color-surface) 96%, transparent); + border-color: color-mix(in srgb, var(--color-accent) 50%, transparent); + transform: translateY(-1px); + box-shadow: 0 10px 28px rgba(0, 0, 0, 0.45); + } + + .card-cta-repo { + display: inline-flex; + align-items: center; + gap: 0.4rem; + padding: 0.25rem 0.7rem; + border-radius: 999px; + border: 1px solid color-mix(in srgb, var(--color-border) 85%, transparent); + background-color: color-mix(in srgb, var(--color-bg) 94%, transparent); + font-size: 0.7rem; + color: var(--color-text-muted); + text-decoration: none; + transition: + color 0.15s ease-out, + border-color 0.15s ease-out, + background-color 0.15s ease-out, + transform 0.15s ease-out, + box-shadow 0.15s ease-out; + } + + .card-cta-repo:hover { + color: var(--color-accent); + border-color: color-mix(in srgb, var(--color-accent) 55%, transparent); + background-color: color-mix(in srgb, var(--color-surface) 96%, transparent); + transform: translateY(-1px); + box-shadow: 0 10px 28px rgba(0, 0, 0, 0.5); + } + + + .card-cta:hover { + color: var(--color-accent); + border-color: color-mix(in srgb, var(--color-accent) 40%, transparent); + background-color: color-mix(in srgb, var(--color-surface) 94%, transparent); + transform: translateY(-1px); + } + + .card-home--post { + position: relative; + overflow: hidden; + background-color: color-mix(in srgb, var(--color-surface) 94%, transparent); + border-left-width: 2px; + border-left-color: color-mix(in srgb, var(--color-border) 80%, transparent); + transition: + transform 0.18s ease-out, + box-shadow 0.18s ease-out, + border-color 0.18s ease-out, + background-color 0.18s ease-out; + } + + .card-home--post::before { + content: ""; + position: absolute; + left: -4px; + top: -20%; + bottom: -20%; + width: 10px; + background: linear-gradient( + to bottom, + transparent, + color-mix(in srgb, var(--color-accent) 70%, transparent), + transparent + ); + opacity: 0; + transform: translateX(-6px); + transition: + opacity 0.22s ease-out, + transform 0.22s ease-out; + pointer-events: none; + } + + .card-home--post:hover { + transform: translateY(-3px); + background-color: color-mix(in srgb, var(--color-surface) 98%, transparent); + border-left-color: color-mix(in srgb, var(--color-accent) 55%, var(--color-border)); + box-shadow: 0 16px 34px rgba(0, 0, 0, 0.65); + } + + .card-home--post:hover::before { + opacity: 0.55; + transform: translateX(0); + } + + .card-home-icon--post { + background-color: color-mix(in srgb, var(--color-bg) 94%, transparent); + } + + .card-badge--soft { + background-color: color-mix(in srgb, var(--color-accent) 16%, transparent); + color: color-mix(in srgb, var(--color-accent) 85%, white); + } + + .card { + border-radius: 0.75rem; + transition: all 0.2s ease-out; + } + + .card-pad { + padding: 1rem; + } + + .card:hover { + box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2); + border-color: color-mix(in srgb, var(--color-accent) 40%, transparent); + } \ No newline at end of file diff --git a/themes/minimal-black/assets/css/components/dock.css b/themes/minimal-black/assets/css/components/dock.css new file mode 100644 index 0000000..46580b8 --- /dev/null +++ b/themes/minimal-black/assets/css/components/dock.css @@ -0,0 +1,134 @@ +/* ========================================================================== + DOCK COMPONENT + Floating action dock with toggle and panel + ========================================================================== */ + +@layer components { + .dock { + position: fixed; + right: 1.5rem; + bottom: 1.6rem; + z-index: 50; + } + + .dock-inner { + display: inline-flex; + align-items: center; + gap: 0.45rem; + padding: 0.3rem 0.5rem; + border-radius: 9999px; + /* border: 1px solid var(--color-border); + background-color: color-mix(in srgb, var(--color-surface) 92%, transparent); + box-shadow: 0 14px 40px rgba(0, 0, 0, 0.55); */ + backdrop-filter: blur(16px); + } + + .dock-panel { + @apply p-1; + display: inline-flex; + align-items: center; + gap: 0.35rem; + max-width: 0; + opacity: 0; + transform: translateX(6px); + overflow: hidden; + transition: max-width 0.18s ease-out, opacity 0.18s ease-out, + transform 0.18s ease-out; + } + + .dock--open .dock-panel { + max-width: 10rem; + opacity: 1; + transform: translateX(0); + } + + .dock-divider { + width: 1px; + height: 1.4rem; + background: linear-gradient( + to bottom, + transparent, + color-mix(in srgb, var(--color-border) 80%, transparent), + transparent + ); + } + + .dock-action { + @apply flex items-center justify-center; + width: 2rem; + height: 2rem; + border-radius: 9999px; + border: 1px solid var(--color-border); + background-color: color-mix(in srgb, var(--color-bg) 75%, transparent); + color: var(--color-text-muted); + cursor: pointer; + transition: background-color 0.16s ease-out, border-color 0.16s ease-out, + color 0.16s ease-out, transform 0.16s ease-out, box-shadow 0.16s ease-out; + } + + .dock-action:hover { + border-color: var(--color-accent); + color: var(--color-accent); + transform: translateY(-1px); + /* box-shadow: 0 8px 26px rgba(0, 0, 0, 0.55); */ + } + + .dock-toggle { + @apply flex items-center justify-center; + width: 2.1rem; + height: 2.1rem; + border-radius: 9999px; + border: 1px solid var(--color-border); + background-color: color-mix(in srgb, var(--color-bg) 85%, transparent); + color: var(--color-text-muted); + cursor: pointer; + transition: background-color 0.16s ease-out, border-color 0.16s ease-out, + transform 0.16s ease-out, box-shadow 0.16s ease-out; + } + + .dock-toggle:hover { + border-color: var(--color-accent); + color: var(--color-accent); + transform: translateY(-1px); + box-shadow: 0 10px 24px rgba(0, 0, 0, 0.5); + } + + .dock-toggle-dots { + position: relative; + display: inline-block; + width: 0.8rem; + height: 0.16rem; + border-radius: 999px; + background-color: var(--color-text-muted); + } + + .dock-toggle-dots::before, + .dock-toggle-dots::after { + content: ""; + position: absolute; + inset-y: 0; + width: 0.16rem; + border-radius: 999px; + background-color: var(--color-text-muted); + } + + .dock-toggle-dots::before { + left: -0.18rem; + } + + .dock-toggle-dots::after { + right: -0.18rem; + } + + .dock--open .dock-toggle-dots, + .dock--open .dock-toggle-dots::before, + .dock--open .dock-toggle-dots::after { + background-color: var(--color-accent); + } + + .btn-primary-sm { + font-size: 0.75rem; + padding: 0.35rem 0.9rem; + border-radius: 999px; + } +} \ No newline at end of file diff --git a/themes/minimal-black/assets/css/components/navigation.css b/themes/minimal-black/assets/css/components/navigation.css new file mode 100644 index 0000000..9e9bd57 --- /dev/null +++ b/themes/minimal-black/assets/css/components/navigation.css @@ -0,0 +1,203 @@ +/* ========================================================================== + LAYOUT & NAVIGATION + Page layouts, headers, footers, navigation links + ========================================================================== */ + + .layout-page { + @apply mx-auto max-w-7xl px-4 py-6 sm:px-6 lg:py-12; + } + + .layout-page-tight { + @apply mx-auto max-w-6xl px-4 py-4 sm:px-6 lg:py-10; + } + + .section-stack { + @apply space-y-10; + } + + .section-stack--home > * + * { + margin-top: 1.75rem; + } + + .section-stack > div + div.posts-section { + border-top: 1px solid color-mix(in srgb, var(--color-border) 60%, transparent); + padding-top: 1.5rem; + } + + /* Headings */ + .heading-page { + @apply text-3xl font-semibold tracking-tight sm:text-4xl; + } + + .heading-section { + @apply text-sm font-semibold tracking-tight; + } + + .eyebrow { + @apply text-[0.65rem] uppercase tracking-[0.22em]; + } + + /* Cards */ + .card { + @apply border border-border bg-surface rounded-xl shadow-sm; + } + + .card-pad { + @apply p-4 sm:p-5; + } + + .card-hover { + @apply transition-transform transition-shadow duration-150 ease-out; + } + + .card-hover:hover { + @apply shadow-lg; + transform: translateY(-1px); + border-color: var(--color-accent); + } + + /* Buttons */ + .btn-primary { + @apply inline-flex items-center rounded-full bg-accent px-4 py-2 text-xs font-medium text-white shadow-sm transition-transform duration-150 ease-out; + } + + .btn-primary:hover { + transform: translateY(-1px); + } + + .btn-ghost { + @apply inline-flex items-center text-xs font-medium text-muted; + } + + .nav-shell { + @apply rounded-full border border-border shadow-md backdrop-blur p-3; + background-color: color-mix(in srgb, var(--color-surface) 82%, transparent); + } + + .nav-shell::before { + content: ""; + position: absolute; + inset-inline: 0; + top: 0; + bottom: 0; + height: 1px; + background: linear-gradient( + 90deg, + transparent, + color-mix(in srgb, var(--color-accent) 80%, transparent), + transparent + ); + opacity: 0.8; + pointer-events: none; + } + + .nav-inner { + @apply flex items-center justify-between px-4 py-2 sm:px-5 sm:py-2.5; + } + + /* Brand logo badge (fallback) */ + .logo-badge { + @apply flex h-8 w-8 items-center justify-center rounded-full bg-accent text-[0.65rem] font-semibold text-white; + } + + /* Nav links */ + .nav-link { + @apply text-[0.78rem] font-medium text-muted; + } + + .nav-link:hover { + color: var(--color-accent); + } + + /* Theme toggle */ + .theme-toggle { + @apply inline-flex h-8 w-8 items-center justify-center rounded-full border border-border bg-bg text-muted shadow-sm text-[0.7rem]; + transition: background-color 0.15s ease-out, color 0.15s ease-out, + transform 0.15s ease-out; + } + + .theme-toggle:hover { + @apply text-accent; + transform: translateY(-1px); + } + + html[data-theme="dark"] .theme-toggle { + background-color: var(--color-surface); + } + + /* Fancy underline link */ + .link-underline { + position: relative; + display: inline-block; + } + + .link-underline::after { + content: ""; + position: absolute; + left: 0; + bottom: -0.15rem; + width: 0; + height: 1px; + background-color: var(--color-accent); + transition: width 0.16s ease-out; + } + + .link-underline:hover::after { + width: 100%; + } + + /* Footer shell */ + .footer-shell { + @apply rounded-2xl border border-border shadow-md backdrop-blur; + position: relative; + overflow: hidden; + background-color: color-mix(in srgb, var(--color-surface) 86%, transparent); + } + + .footer-shell::before { + content: ""; + position: absolute; + inset-inline: 0; + top: 0; + height: 1px; + background: linear-gradient( + 90deg, + transparent, + color-mix(in srgb, var(--color-accent) 80%, transparent), + transparent + ); + opacity: 0.8; + pointer-events: none; + } + + .footer-inner { + @apply flex flex-col gap-3 px-4 py-3 text-[0.72rem] sm:flex-row sm:items-center sm:justify-between sm:px-5; + } + + .footer-links { + @apply flex flex-wrap items-center gap-3; + } + + .footer-link { + @apply inline-flex items-center gap-1 text-[0.75rem] font-medium text-muted transition-all duration-150 ease-out; + } + + .footer-link:hover { + color: var(--color-accent); + } + + .footer-small { + @apply text-[0.72rem] text-muted leading-relaxed; + } + + .footer-float:hover { + color: var(--color-accent); + transform: translateY(-1px); + } + + .footer-float:hover i { + filter: drop-shadow( + 0 0 2px color-mix(in srgb, var(--color-accent) 60%, transparent) + ); + } + diff --git a/themes/minimal-black/assets/css/components/search.css b/themes/minimal-black/assets/css/components/search.css new file mode 100644 index 0000000..6842705 --- /dev/null +++ b/themes/minimal-black/assets/css/components/search.css @@ -0,0 +1,289 @@ +/* ========================================================================== + SEARCH OVERLAY + Search modal, search results, empty states + ========================================================================== */ + + .search-overlay { + position: fixed; + inset: 0; + z-index: 40; + display: none; + align-items: center; + justify-content: center; + opacity: 0; + pointer-events: none; + transition: opacity 0.18s ease-out; + } + + .search-overlay--open { + opacity: 1; + display: block; + pointer-events: auto; + } + + .search-overlay-backdrop { + position: absolute; + inset: 0; + background: radial-gradient( + circle at top, + rgba(0, 0, 0, 0.4), + transparent 55% + ), + rgba(0, 0, 0, 0.7); + backdrop-filter: blur(12px); + } + + .search-panel { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + + width: 100%; + max-width: 36rem; + margin-inline: 1.5rem; + + border-radius: 1.1rem; + border: 1px solid var(--color-border); + background-color: color-mix(in srgb, var(--color-bg) 96%, transparent); + box-shadow: 0 26px 70px rgba(0, 0, 0, 0.85); + padding: 0.8rem 0.9rem 0.6rem; + display: flex; + flex-direction: column; + gap: 0.6rem; + } + + .search-overlay--open .search-panel { + animation: search-panel-in 0.18s ease-out; + } + + .search-overlay-backdrop { + position: absolute; + inset: 0; + background: radial-gradient( + circle at top, + rgba(0, 0, 0, 0.4), + transparent 55% + ), + rgba(0, 0, 0, 0.7); + backdrop-filter: blur(12px); + } + + .search-overlay .search-overlay--open .search-overlay-backdrop { + animation: search-backdrop-in 0.18s ease-out; + } + + .search-overlay--closing .search-panel { + animation: search-panel-out 0.18s ease-in forwards; + } + + .search-overlay--closing .search-overlay-backdrop { + animation: search-backdrop-out 0.18s ease-in forwards; + } + + .search-panel-header { + display: flex; + align-items: center; + gap: 0.6rem; + } + + .search-input-wrap { + flex: 1; + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.45rem 0.8rem; + border-radius: 999px; + border: 1px solid var(--color-border); + background-color: color-mix(in srgb, var(--color-surface) 96%, transparent); + } + + .search-input { + flex: 1; + border: none; + outline: none; + font-size: 0.82rem; + background: transparent; + color: var(--color-text); + } + + .search-input::placeholder { + color: var(--color-text-muted); + } + + .search-close { + width: 2rem; + height: 2rem; + border-radius: 999px; + border: 1px solid var(--color-border); + background-color: color-mix(in srgb, var(--color-bg) 90%, transparent); + color: var(--color-text-muted); + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + transition: background-color 0.16s ease-out, border-color 0.16s ease-out, + color 0.16s ease-out, transform 0.16s ease-out; + } + + .search-close:hover { + border-color: var(--color-accent); + color: var(--color-accent); + transform: translateY(-1px); + } + + .search-panel-body { + border-radius: 0.9rem; + border: 1px solid color-mix(in srgb, var(--color-border) 70%, transparent); + background-color: color-mix(in srgb, var(--color-surface) 96%, transparent); + padding: 0.5rem 0.4rem 0.4rem; + display: flex; + flex-direction: column; + } + + .search-results { + max-height: 18rem; + padding: 0.25rem 0.15rem 0.4rem; + overflow-y: auto; + } + + .search-empty-state { + display: flex; + flex-direction: column; + align-items: center; + padding: 1.2rem 0.5rem 1.4rem; + gap: 0.4rem; + } + + .search-empty-icon { + width: 2.3rem; + height: 2.3rem; + border-radius: 999px; + border: 1px solid var(--color-border); + background-color: color-mix(in srgb, var(--color-bg) 92%, transparent); + display: flex; + align-items: center; + justify-content: center; + } + + .search-empty-title { + font-size: 0.86rem; + font-weight: 600; + color: var(--color-text); + } + + .search-empty-subtitle { + font-size: 0.76rem; + color: var(--color-text-muted); + } + + .search-footer-hints { + display: flex; + flex-wrap: wrap; + gap: 0.45rem; + border-top: 1px solid + color-mix(in srgb, var(--color-border) 75%, transparent); + padding: 0.4rem 0.5rem 0.1rem; + margin-top: 0.1rem; + } + + .search-hint-key { + display: inline-flex; + align-items: center; + gap: 0.25rem; + font-size: 0.68rem; + color: var(--color-text-muted); + } + + .search-hint-key span:first-child, + .search-hint-key span:nth-child(2) { + padding: 0.1rem 0.3rem; + border-radius: 0.3rem; + border: 1px solid color-mix(in srgb, var(--color-border) 80%, transparent); + background-color: color-mix(in srgb, var(--color-bg) 96%, transparent); + font-size: 0.65rem; + } + + .search-hint-key.search-hint-right { + margin-left: auto; + } + + .search-result-item { + display: block; + padding: 0.42rem 0.55rem; + border-radius: 0.6rem; + text-decoration: none; + transition: background-color 0.12s ease-out, transform 0.12s ease-out; + } + + .search-result-item:hover { + background-color: color-mix(in srgb, var(--color-surface) 92%, transparent); + transform: translateY(-1px); + } + + .search-result-title { + font-size: 0.8rem; + font-weight: 500; + color: var(--color-text); + } + + .search-result-meta { + margin-top: 0.15rem; + font-size: 0.7rem; + color: var(--color-text-muted); + } + .search-footer-hints { + display: flex; + flex-wrap: wrap; + gap: 0.45rem; + border-top: 1px solid + color-mix(in srgb, var(--color-border) 75%, transparent); + padding: 0.4rem 0.5rem 0.1rem; + margin-top: 0.1rem; + } + + .search-hint-key { + display: inline-flex; + align-items: center; + gap: 0.25rem; + font-size: 0.68rem; + color: var(--color-text-muted); + } + + .search-hint-key span:first-child, + .search-hint-key span:nth-child(2) { + padding: 0.1rem 0.3rem; + border-radius: 0.3rem; + border: 1px solid color-mix(in srgb, var(--color-border) 80%, transparent); + background-color: color-mix(in srgb, var(--color-bg) 96%, transparent); + font-size: 0.65rem; + } + + .search-hint-key.search-hint-right { + margin-left: auto; + } + + .search-result-item { + display: block; + padding: 0.42rem 0.55rem; + border-radius: 0.6rem; + text-decoration: none; + transition: background-color 0.12s ease-out, transform 0.12s ease-out; + } + + .search-result-item:hover { + background-color: color-mix(in srgb, var(--color-surface) 92%, transparent); + transform: translateY(-1px); + } + + .search-result-title { + font-size: 0.8rem; + font-weight: 500; + color: var(--color-text); + } + + .search-result-meta { + margin-top: 0.15rem; + font-size: 0.7rem; + color: var(--color-text-muted); + } diff --git a/themes/minimal-black/assets/css/components/tech-marquee.css b/themes/minimal-black/assets/css/components/tech-marquee.css new file mode 100644 index 0000000..b42c905 --- /dev/null +++ b/themes/minimal-black/assets/css/components/tech-marquee.css @@ -0,0 +1,111 @@ +/* ========================================================================== + TECH MARQUEE + Technology carousel/strip component + ========================================================================== */ + + .tech-carousel { + display: flex; + gap: 0.5rem; + padding: 0.3rem 0.1rem 0.1rem; + overflow-x: auto; + scroll-snap-type: x mandatory; + } + + .tech-carousel::-webkit-scrollbar { + height: 4px; + } + + .tech-carousel::-webkit-scrollbar-thumb { + border-radius: 999px; + background-color: rgba(255, 255, 255, 0.12); + } + + .tech-pill { + scroll-snap-align: start; + display: inline-flex; + align-items: center; + gap: 0.4rem; + padding: 0.35rem 0.7rem; + border-radius: 999px; + border: 1px solid var(--color-border); + background-color: color-mix(in srgb, var(--color-surface) 92%, transparent); + box-shadow: 0 10px 28px rgba(0, 0, 0, 0.28); + white-space: nowrap; + } + + .badge-available { + @apply inline-flex items-center gap-1.5; + padding: 0.25rem 0.7rem; + border-radius: 999px; + background-color: var(--color-accent); + color: white; + font-size: 0.7rem; + font-weight: 600; + box-shadow: 0 0 0.4rem rgba(0, 0, 0, 0.4); + } + + /* --- Tech marquee --- */ + .tech-icon { + font-size: 1.5rem; + } + + .tech-strip { + position: relative; + overflow: hidden; + padding-block: 0.4rem; + } + + .tech-strip-track { + @apply py-2; + display: inline-flex; + align-items: center; + white-space: nowrap; + animation-name: tech-marquee; + animation-timing-function: linear; + animation-iteration-count: infinite; + animation-play-state: running; + } + + .tech-strip-track { + gap: 1.5rem; + } + + .tech-strip--wide .tech-strip-track--primary { + animation-duration: 40s; + } + + .tech-strip--wide .tech-strip-track--secondary { + animation-duration: 40s; + animation-direction: reverse; + } + + .tech-strip--compact .tech-strip-track--primary { + animation-duration: 40s; + } + + .tech-strip--compact .tech-strip-track--secondary { + animation-duration: 48s; + animation-direction: reverse; + } + + .tech-strip--compact .tech-strip-track { + gap: 1.1rem; + } + + /* pause both rows on hover */ + .tech-strip:hover .tech-strip-track { + animation-play-state: paused; + } + + .tech-strip-item { + display: inline-flex; + align-items: center; + gap: 0.4rem; + opacity: 0.88; + transition: opacity 0.15s ease-out, transform 0.15s ease-out; + } + + .tech-strip-item:hover { + opacity: 1; + transform: translateY(-1px); + } diff --git a/themes/minimal-black/assets/css/content/markdown.css b/themes/minimal-black/assets/css/content/markdown.css new file mode 100644 index 0000000..f5b8252 --- /dev/null +++ b/themes/minimal-black/assets/css/content/markdown.css @@ -0,0 +1,1372 @@ +/* ========================================================================== + MARKDOWN CONTENT STYLES + Typography, blockquotes, lists, code blocks, tables, alerts + ========================================================================== */ + + .markdown-body { + max-width: 85ch; + margin-inline: auto; + font-size: 0.95rem; + line-height: 1.75; + color: var(--color-text); + overflow-wrap: break-word; + word-wrap: break-word; + min-width: 0; + } + + /* rhythm */ + .markdown-body > * { + margin-top: 0; + margin-bottom: 0; + } + + .markdown-body > * + * { + margin-top: 1.25rem; + } + + /* paragraphs */ + .markdown-body p { + color: var(--color-text); + line-height: 1.75; + } + + /* headings */ + .markdown-body h1, + .markdown-body h2, + .markdown-body h3, + .markdown-body h4, + .markdown-body h5, + .markdown-body h6 { + color: var(--color-text); + font-weight: 650; + line-height: 1.3; + letter-spacing: -0.01em; + margin-top: 2rem; + margin-bottom: 0.75rem; + } + + .markdown-body h1 { + font-size: 2rem; + margin-top: 0; + } + + .markdown-body h2 { + font-size: 1.5rem; + padding-bottom: 0.4rem; + border-bottom: 1px solid color-mix(in srgb, var(--color-border) 60%, transparent); + margin-top: 2.5rem; + } + + .markdown-body h3 { + font-size: 1.25rem; + margin-top: 2rem; + } + + .markdown-body h4 { + font-size: 1.1rem; + } + + .markdown-body h5 { + font-size: 1rem; + } + + .markdown-body h6 { + font-size: 0.85rem; + text-transform: uppercase; + letter-spacing: 0.1em; + color: var(--color-text-muted); + } + + /* links */ + .markdown-body a { + color: var(--color-accent); + text-decoration: none; + border-bottom: 1px solid color-mix(in srgb, var(--color-accent) 35%, transparent); + padding-bottom: 0.05rem; + transition: color 0.15s ease-out, border-color 0.15s ease-out; + word-wrap: break-word; + overflow-wrap: break-word; + word-break: break-word; + } + + .markdown-body a:hover { + color: color-mix(in srgb, var(--color-accent) 85%, white); + border-bottom-color: var(--color-accent); + } + + /* strong and emphasis */ + .markdown-body strong { + font-weight: 650; + color: var(--color-text); + } + + .markdown-body em { + font-style: italic; + } + + /* lists */ + .markdown-body ul, + .markdown-body ol { + padding-left: 1.75rem; + margin-top: 1rem; + margin-bottom: 1rem; + } + + .markdown-body li { + margin: 0.5rem 0; + line-height: 1.7; + padding-left: 0.5rem; + position: relative; + transition: background-color 0.15s ease-out; + } + + .markdown-body li:hover { + background-color: color-mix(in srgb, var(--color-surface) 30%, transparent); + border-radius: 0.35rem; + } + + .markdown-body li > ul, + .markdown-body li > ol { + margin-top: 0.5rem; + margin-bottom: 0.5rem; + } + + /* Unordered list markers */ + .markdown-body ul li::marker { + color: var(--color-accent); + font-size: 1.1em; + } + + .markdown-body ul { + list-style-type: disc; + } + + .markdown-body ul ul { + list-style-type: circle; + } + + .markdown-body ul ul ul { + list-style-type: square; + } + + /* Ordered list markers */ + .markdown-body ol { + list-style-type: decimal; + } + + .markdown-body ol li::marker { + color: var(--color-accent); + font-weight: 700; + font-size: 0.95em; + } + + .markdown-body ol ol { + list-style-type: lower-alpha; + } + + .markdown-body ol ol ol { + list-style-type: lower-roman; + } + + /* Better spacing for nested lists */ + .markdown-body li > p { + margin-top: 0.35rem; + margin-bottom: 0.35rem; + } + + .markdown-body li:first-child { + margin-top: 0; + } + + .markdown-body li:last-child { + margin-bottom: 0; + } + + /* definition lists */ + .markdown-body dl { + margin: 1.5rem 0; + padding: 1.25rem; + border-left: 3px solid var(--color-accent); + background: linear-gradient( + to right, + color-mix(in srgb, var(--color-accent) 8%, transparent), + color-mix(in srgb, var(--color-surface) 40%, transparent) 3rem + ); + border-radius: 0.6rem; + } + + .markdown-body dt { + font-weight: 650; + color: var(--color-text); + margin-top: 1rem; + margin-bottom: 0.5rem; + font-size: 1rem; + display: flex; + align-items: center; + gap: 0.5rem; + } + + .markdown-body dt:first-child { + margin-top: 0; + } + + .markdown-body dt::before { + content: '▸'; + color: var(--color-accent); + font-size: 0.9em; + font-weight: bold; + } + + .markdown-body dd { + margin-left: 1.5rem; + margin-bottom: 0.75rem; + padding-left: 1rem; + border-left: 2px solid color-mix(in srgb, var(--color-border) 60%, transparent); + color: var(--color-text-muted); + line-height: 1.7; + } + + .markdown-body dd:last-child { + margin-bottom: 0; + } + + .markdown-body dd > p { + margin: 0.25rem 0; + } + + .markdown-body dd > p:first-child { + margin-top: 0; + } + + .markdown-body dd > p:last-child { + margin-bottom: 0; + } + + /* task lists */ + .markdown-body ul.contains-task-list { + list-style: none; + padding-left: 0; + } + + .markdown-body .task-list-item { + display: flex; + align-items: flex-start; + gap: 0.65rem; + padding: 0.5rem 0.75rem; + margin: 0.35rem 0; + border-radius: 0.45rem; + transition: all 0.15s ease-out; + background-color: color-mix(in srgb, var(--color-surface) 20%, transparent); + } + + .markdown-body .task-list-item:hover { + background-color: color-mix(in srgb, var(--color-surface) 50%, transparent); + transform: translateX(3px); + } + + .markdown-body .task-list-item input[type="checkbox"] { + margin-top: 0.35rem; + flex-shrink: 0; + width: 1.1rem; + height: 1.1rem; + cursor: pointer; + accent-color: var(--color-accent); + border-radius: 0.25rem; + } + + .markdown-body .task-list-item input[type="checkbox"]:checked { + accent-color: #22c55e; + } + + .markdown-body .task-list-item input[type="checkbox"]:checked + * { + color: var(--color-text-muted); + text-decoration: line-through; + text-decoration-color: var(--color-text-muted); + } + + /* blockquote */ + .markdown-body blockquote, + .markdown-body .md-blockquote { + margin: 1.75rem 0; + padding: 1.25rem 1.5rem; + border-left: 4px solid var(--color-accent); + background: linear-gradient( + to right, + color-mix(in srgb, var(--color-accent) 8%, transparent), + color-mix(in srgb, var(--color-surface) 60%, transparent) 3rem + ); + border-radius: 0.6rem; + color: var(--color-text); + font-style: italic; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); + position: relative; + } + + .markdown-body blockquote::before, + .markdown-body .md-blockquote::before { + content: '"'; + position: absolute; + left: 0.75rem; + top: 0.5rem; + font-size: 2.5rem; + line-height: 1; + color: color-mix(in srgb, var(--color-accent) 25%, transparent); + font-family: Georgia, serif; + font-weight: bold; + } + + .markdown-body blockquote p, + .markdown-body .md-blockquote p { + color: var(--color-text); + font-size: 0.98rem; + line-height: 1.7; + } + + .markdown-body blockquote p + p, + .markdown-body .md-blockquote p + p { + margin-top: 0.85rem; + } + + .markdown-body blockquote strong, + .markdown-body .md-blockquote strong { + color: var(--color-text); + font-weight: 650; + } + + /* GitHub-style alerts */ + .markdown-body .md-alert { + margin: 1.75rem 0; + padding: 1rem 1.25rem 1rem 3rem; + border-radius: 0.6rem; + border-left: 4px solid; + position: relative; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); + } + + .markdown-body .md-alert::before { + content: ''; + position: absolute; + left: 1rem; + top: 1.1rem; + width: 1.25rem; + height: 1.25rem; + background-size: contain; + background-repeat: no-repeat; + background-position: center; + } + + .markdown-body .md-alert p:first-child { + margin-top: 0; + } + + .markdown-body .md-alert p:last-child { + margin-bottom: 0; + } + + /* Note alert (blue) */ + .markdown-body .md-alert-note { + background: linear-gradient( + to right, + color-mix(in srgb, #3b82f6 10%, transparent), + color-mix(in srgb, var(--color-surface) 60%, transparent) 3rem + ); + border-left-color: #3b82f6; + } + + .markdown-body .md-alert-note::before { + content: 'ℹ'; + display: flex; + align-items: center; + justify-content: center; + font-size: 1rem; + font-weight: bold; + color: #3b82f6; + background: none; + } + + /* Tip alert (green) */ + .markdown-body .md-alert-tip { + background: linear-gradient( + to right, + color-mix(in srgb, #22c55e 10%, transparent), + color-mix(in srgb, var(--color-surface) 60%, transparent) 3rem + ); + border-left-color: #22c55e; + } + + .markdown-body .md-alert-tip::before { + content: '💡'; + font-size: 1.1rem; + } + + /* Important alert (purple) */ + .markdown-body .md-alert-important { + background: linear-gradient( + to right, + color-mix(in srgb, var(--color-accent) 12%, transparent), + color-mix(in srgb, var(--color-surface) 60%, transparent) 3rem + ); + border-left-color: var(--color-accent); + } + + .markdown-body .md-alert-important::before { + content: '⚡'; + font-size: 1.1rem; + } + + /* Warning alert (yellow/orange) */ + .markdown-body .md-alert-warning { + background: linear-gradient( + to right, + color-mix(in srgb, #f59e0b 10%, transparent), + color-mix(in srgb, var(--color-surface) 60%, transparent) 3rem + ); + border-left-color: #f59e0b; + } + + .markdown-body .md-alert-warning::before { + content: '⚠'; + display: flex; + align-items: center; + justify-content: center; + font-size: 1.1rem; + color: #f59e0b; + background: none; + } + + /* Danger/Caution alert (red) */ + .markdown-body .md-alert-danger, + .markdown-body .md-alert-caution { + background: linear-gradient( + to right, + color-mix(in srgb, #ef4444 10%, transparent), + color-mix(in srgb, var(--color-surface) 60%, transparent) 3rem + ); + border-left-color: #ef4444; + } + + .markdown-body .md-alert-danger::before, + .markdown-body .md-alert-caution::before { + content: '🛑'; + font-size: 1.1rem; + } + + /* inline code */ + .markdown-body :not(pre) > code { + font-family: 'JetBrains Mono', 'Fira Code', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; + font-size: 0.88em; + padding: 0.15rem 0.4rem; + border-radius: 0.35rem; + background: color-mix(in srgb, var(--color-surface) 80%, transparent); + border: 1px solid color-mix(in srgb, var(--color-border) 70%, transparent); + color: color-mix(in srgb, var(--color-accent) 90%, white); + } + + html[data-theme="light"] .markdown-body :not(pre) > code { + background: #f3f4f6; + border-color: #d1d5db; + color: #7c3aed; + } + + /* CODE BLOCKS - This is the important part */ + .markdown-body pre { + margin: 1.5rem 0; + padding: 0; + overflow: hidden; + overflow-x: auto; + border-radius: 0.75rem; + border: 1px solid color-mix(in srgb, var(--color-border) 70%, transparent); + background: color-mix(in srgb, var(--color-surface) 40%, transparent); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); + max-width: 100%; + } + + html[data-theme="light"] .markdown-body pre { + background: #f9fafb; + border-color: #e5e7eb; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); + } + + .markdown-body pre code { + display: block; + padding: 1.25rem 1.5rem; + overflow-x: auto; + font-family: 'JetBrains Mono', 'Fira Code', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; + font-size: 0.88rem; + line-height: 1.6; + color: var(--color-text); + background: transparent; + border: none; + } + + /* Code block with header (language badge) */ + .markdown-body .highlight { + position: relative; + background: color-mix(in srgb, var(--color-surface) 40%, transparent); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); + overflow: hidden; + } + + .markdown-body .highlight pre { + margin: 0; + border: none; + border-radius: 0; + box-shadow: none; + } + + /* Custom code block wrapper (if you want header with language/copy button) */ + .mb-codeblock { + margin: 1.5rem 0; + border: 1px solid color-mix(in srgb, var(--color-border) 70%, transparent); + border-radius: 0.75rem; + overflow: hidden; + background: color-mix(in srgb, var(--color-surface) 40%, transparent); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); + } + + .mb-codeblock-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 0.75rem 1rem; + border-bottom: 1px solid color-mix(in srgb, var(--color-border) 60%, transparent); + background: color-mix(in srgb, var(--color-bg) 50%, transparent); + } + + .mb-codeblock-left { + display: flex; + align-items: center; + gap: 0.5rem; + } + + .mb-codeblock-label { + color: var(--color-text-muted); + font-size: 0.75rem; + font-weight: 500; + letter-spacing: 0.02em; + } + + .mb-codeblock-badge { + font-size: 0.7rem; + font-weight: 600; + padding: 0.2rem 0.5rem; + border-radius: 0.35rem; + background: color-mix(in srgb, var(--color-accent) 20%, transparent); + color: var(--color-accent); + text-transform: uppercase; + letter-spacing: 0.05em; + } + + .mb-codeblock-actions { + display: flex; + gap: 0.25rem; + } + + .mb-codeblock-actions button { + background: transparent; + border: 1px solid color-mix(in srgb, var(--color-border) 70%, transparent); + color: var(--color-text-muted); + cursor: pointer; + font-size: 0.7rem; + padding: 0.3rem 0.6rem; + border-radius: 0.35rem; + transition: all 0.15s ease-out; + display: flex; + align-items: center; + gap: 0.35rem; + } + + .mb-codeblock-actions button:hover { + color: var(--color-accent); + background: color-mix(in srgb, var(--color-accent) 15%, transparent); + border-color: var(--color-accent); + } + + .mb-codeblock-content { + position: relative; + } + + .mb-codeblock-content pre { + margin: 0; + padding: 1.25rem 1.5rem; + overflow-x: auto; + border: none; + border-radius: 0; + background: transparent; + box-shadow: none; + } + + .mb-codeblock-content code { + font-family: 'JetBrains Mono', 'Fira Code', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; + font-size: 0.88rem; + line-height: 1.6; + } + + /* tables */ + .markdown-body table { + width: 100%; + margin: 1.5rem 0; + border-collapse: collapse; + font-size: 0.9rem; + border-radius: 0.75rem; + overflow: hidden; + border: 1px solid color-mix(in srgb, var(--color-border) 70%, transparent); + } + + .markdown-body thead { + background: color-mix(in srgb, var(--color-surface) 60%, transparent); + } + + .markdown-body th { + padding: 0.75rem 1rem; + text-align: left; + font-weight: 600; + color: var(--color-text); + border-bottom: 1px solid color-mix(in srgb, var(--color-border) 70%, transparent); + } + + .markdown-body td { + padding: 0.75rem 1rem; + border-bottom: 1px solid color-mix(in srgb, var(--color-border) 60%, transparent); + } + + .markdown-body tbody tr:last-child td { + border-bottom: none; + } + + .markdown-body tbody tr:hover { + background: color-mix(in srgb, var(--color-surface) 30%, transparent); + } + + /* hr */ + .markdown-body hr { + border: 0; + border-top: 1px solid color-mix(in srgb, var(--color-border) 70%, transparent); + margin: 2rem 0; + } + + /* Custom link with external indicator */ + .markdown-body .md-link { + position: relative; + } + + .markdown-body a.md-link[target="_blank"]::after { + display: inline-block; + margin-left: 0.25rem; + font-size: 0.85em; + opacity: 0.6; + transition: all 0.15s ease-out; + } + + .markdown-body a.md-link[target="_blank"]:hover::after { + opacity: 1; + transform: translate(2px, -2px); + } + + /* Heading anchors with hover effect */ + .markdown-body .md-heading-anchor { + position: relative; + color: inherit; + text-decoration: none; + border-bottom: none; + padding-bottom: 0; + } + + .markdown-body .md-heading-anchor::before { + content: '#'; + position: absolute; + left: -1.5rem; + opacity: 0; + color: var(--color-accent); + font-weight: 400; + transition: opacity 0.15s ease-out; + } + + .markdown-body h1:hover .md-heading-anchor::before, + .markdown-body h2:hover .md-heading-anchor::before, + .markdown-body h3:hover .md-heading-anchor::before, + .markdown-body h4:hover .md-heading-anchor::before, + .markdown-body h5:hover .md-heading-anchor::before, + .markdown-body h6:hover .md-heading-anchor::before { + opacity: 1; + } + + .markdown-body .md-heading-anchor:hover { + color: var(--color-accent); + } + + /* Table wrapper for horizontal scrolling */ + .markdown-body .table-wrap { + margin: 1.75rem 0; + overflow-x: auto; + border-radius: 0.75rem; + border: 1px solid color-mix(in srgb, var(--color-border) 70%, transparent); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + } + + .markdown-body .table-wrap table { + margin: 0; + border: none; + border-radius: 0; + } + + /* Custom scrollbar for table wrapper */ + .markdown-body .table-wrap::-webkit-scrollbar { + height: 8px; + } + + .markdown-body .table-wrap::-webkit-scrollbar-track { + background: color-mix(in srgb, var(--color-bg) 30%, transparent); + border-radius: 0 0 0.75rem 0.75rem; + } + + .markdown-body .table-wrap::-webkit-scrollbar-thumb { + background: color-mix(in srgb, var(--color-accent) 50%, transparent); + border-radius: 4px; + } + + .markdown-body .table-wrap::-webkit-scrollbar-thumb:hover { + background: var(--color-accent); + } + + /* images */ + .markdown-body img { + display: block; + max-width: 100%; + height: auto; + margin: 1.5rem auto; + border-radius: 0.75rem; + border: 1px solid color-mix(in srgb, var(--color-border) 60%, transparent); + } + + .markdown-body figure { + margin: 1.5rem 0; + text-align: center; + } + + .markdown-body figcaption { + margin-top: 0.5rem; + font-size: 0.85rem; + color: var(--color-text-muted); + font-style: italic; + } + + /* Custom image wrapper with lightbox */ + .markdown-body .md-image { + margin: 2rem 0; + text-align: center; + } + + .markdown-body .md-image a { + display: inline-block; + position: relative; + overflow: hidden; + border-radius: 0.85rem; + border: 1px solid color-mix(in srgb, var(--color-border) 60%, transparent); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); + transition: all 0.25s ease-out; + border-bottom: none; + padding-bottom: 0; + } + + .markdown-body .md-image a::before { + content: ''; + position: absolute; + inset: 0; + background: linear-gradient( + 135deg, + color-mix(in srgb, var(--color-accent) 0%, transparent), + color-mix(in srgb, var(--color-accent) 15%, transparent) + ); + opacity: 0; + transition: opacity 0.25s ease-out; + pointer-events: none; + z-index: 1; + } + + .markdown-body .md-image a::after { + content: '🔍'; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%) scale(0.8); + font-size: 2.5rem; + opacity: 0; + transition: all 0.25s ease-out; + z-index: 2; + pointer-events: none; + filter: drop-shadow(0 2px 8px rgba(0, 0, 0, 0.5)); + } + + .markdown-body .md-image a:hover { + transform: translateY(-4px); + box-shadow: 0 12px 28px rgba(0, 0, 0, 0.25); + border-color: color-mix(in srgb, var(--color-accent) 50%, transparent); + } + + .markdown-body .md-image a:hover::before { + opacity: 1; + } + + .markdown-body .md-image a:hover::after { + opacity: 1; + transform: translate(-50%, -50%) scale(1); + } + + .markdown-body .md-image img { + margin: 0; + border: none; + border-radius: 0.75rem; + display: block; + transition: all 0.25s ease-out; + } + + .markdown-body .md-image a:hover img { + filter: brightness(0.85); + } + + .markdown-body .md-image figcaption { + display: none; + } + + /* SYNTAX HIGHLIGHTING - Dark theme colors */ + /* These will be applied by Hugo's Chroma */ + .markdown-body .chroma { + background: transparent; + } + + .markdown-body .chroma .err { color: #f87171; } + .markdown-body .chroma .lntd { padding: 0; } + .markdown-body .chroma .lntable { border-spacing: 0; } + + /* Chroma syntax colors for dark theme */ + html[data-theme="dark"] .markdown-body .chroma .k { color: #c084fc; } /* keyword - purple */ + html[data-theme="dark"] .markdown-body .chroma .kc { color: #c084fc; } + html[data-theme="dark"] .markdown-body .chroma .kd { color: #c084fc; } + html[data-theme="dark"] .markdown-body .chroma .kn { color: #c084fc; } + html[data-theme="dark"] .markdown-body .chroma .kp { color: #c084fc; } + html[data-theme="dark"] .markdown-body .chroma .kr { color: #c084fc; } + html[data-theme="dark"] .markdown-body .chroma .kt { color: #c084fc; } + + html[data-theme="dark"] .markdown-body .chroma .n { color: #e5e7eb; } + html[data-theme="dark"] .markdown-body .chroma .na { color: #fbbf24; } + html[data-theme="dark"] .markdown-body .chroma .nb { color: #60a5fa; } + html[data-theme="dark"] .markdown-body .chroma .nc { color: #fbbf24; } + html[data-theme="dark"] .markdown-body .chroma .nf { color: #60a5fa; } + html[data-theme="dark"] .markdown-body .chroma .nn { color: #fbbf24; } + + html[data-theme="dark"] .markdown-body .chroma .s { color: #86efac; } + html[data-theme="dark"] .markdown-body .chroma .s1 { color: #86efac; } + html[data-theme="dark"] .markdown-body .chroma .s2 { color: #86efac; } + html[data-theme="dark"] .markdown-body .chroma .sb { color: #86efac; } + html[data-theme="dark"] .markdown-body .chroma .sc { color: #86efac; } + html[data-theme="dark"] .markdown-body .chroma .sd { color: #86efac; } + html[data-theme="dark"] .markdown-body .chroma .se { color: #fbbf24; } + html[data-theme="dark"] .markdown-body .chroma .si { color: #fbbf24; } + html[data-theme="dark"] .markdown-body .chroma .sr { color: #86efac; } + html[data-theme="dark"] .markdown-body .chroma .ss { color: #86efac; } + + html[data-theme="dark"] .markdown-body .chroma .m { color: #fb923c; } + html[data-theme="dark"] .markdown-body .chroma .mb { color: #fb923c; } + html[data-theme="dark"] .markdown-body .chroma .mf { color: #fb923c; } + html[data-theme="dark"] .markdown-body .chroma .mh { color: #fb923c; } + html[data-theme="dark"] .markdown-body .chroma .mi { color: #fb923c; } + html[data-theme="dark"] .markdown-body .chroma .mo { color: #fb923c; } + + html[data-theme="dark"] .markdown-body .chroma .o { color: #c084fc; } + html[data-theme="dark"] .markdown-body .chroma .ow { color: #c084fc; } + + html[data-theme="dark"] .markdown-body .chroma .c { color: #6b7280; font-style: italic; } + html[data-theme="dark"] .markdown-body .chroma .c1 { color: #6b7280; font-style: italic; } + html[data-theme="dark"] .markdown-body .chroma .cm { color: #6b7280; font-style: italic; } + + html[data-theme="dark"] .markdown-body .chroma .p { color: #9ca3af; } + + html[data-theme="dark"] .markdown-body .chroma .g { color: #ef4444; } + html[data-theme="dark"] .markdown-body .chroma .gd { color: #ef4444; } + html[data-theme="dark"] .markdown-body .chroma .ge { font-style: italic; } + html[data-theme="dark"] .markdown-body .chroma .gi { color: #22c55e; } + html[data-theme="dark"] .markdown-body .chroma .gs { font-weight: bold; } + + /* SYNTAX HIGHLIGHTING - Light theme colors */ + html[data-theme="light"] .markdown-body .chroma .k { color: #7c3aed; } /* keyword - purple */ + html[data-theme="light"] .markdown-body .chroma .kc { color: #7c3aed; } + html[data-theme="light"] .markdown-body .chroma .kd { color: #7c3aed; } + html[data-theme="light"] .markdown-body .chroma .kn { color: #7c3aed; } + html[data-theme="light"] .markdown-body .chroma .kp { color: #7c3aed; } + html[data-theme="light"] .markdown-body .chroma .kr { color: #7c3aed; } + html[data-theme="light"] .markdown-body .chroma .kt { color: #7c3aed; } + + html[data-theme="light"] .markdown-body .chroma .n { color: #1f2937; } + html[data-theme="light"] .markdown-body .chroma .na { color: #d97706; } + html[data-theme="light"] .markdown-body .chroma .nb { color: #2563eb; } + html[data-theme="light"] .markdown-body .chroma .nc { color: #d97706; } + html[data-theme="light"] .markdown-body .chroma .nf { color: #2563eb; } + html[data-theme="light"] .markdown-body .chroma .nn { color: #d97706; } + + html[data-theme="light"] .markdown-body .chroma .s { color: #16a34a; } + html[data-theme="light"] .markdown-body .chroma .s1 { color: #16a34a; } + html[data-theme="light"] .markdown-body .chroma .s2 { color: #16a34a; } + html[data-theme="light"] .markdown-body .chroma .sb { color: #16a34a; } + html[data-theme="light"] .markdown-body .chroma .sc { color: #16a34a; } + html[data-theme="light"] .markdown-body .chroma .sd { color: #16a34a; } + html[data-theme="light"] .markdown-body .chroma .se { color: #d97706; } + html[data-theme="light"] .markdown-body .chroma .si { color: #d97706; } + html[data-theme="light"] .markdown-body .chroma .sr { color: #16a34a; } + html[data-theme="light"] .markdown-body .chroma .ss { color: #16a34a; } + + html[data-theme="light"] .markdown-body .chroma .m { color: #dc2626; } + html[data-theme="light"] .markdown-body .chroma .mb { color: #dc2626; } + html[data-theme="light"] .markdown-body .chroma .mf { color: #dc2626; } + html[data-theme="light"] .markdown-body .chroma .mh { color: #dc2626; } + html[data-theme="light"] .markdown-body .chroma .mi { color: #dc2626; } + html[data-theme="light"] .markdown-body .chroma .mo { color: #dc2626; } + + html[data-theme="light"] .markdown-body .chroma .o { color: #7c3aed; } + html[data-theme="light"] .markdown-body .chroma .ow { color: #7c3aed; } + + html[data-theme="light"] .markdown-body .chroma .c { color: #6b7280; font-style: italic; } + html[data-theme="light"] .markdown-body .chroma .c1 { color: #6b7280; font-style: italic; } + html[data-theme="light"] .markdown-body .chroma .cm { color: #6b7280; font-style: italic; } + + html[data-theme="light"] .markdown-body .chroma .p { color: #4b5563; } + + html[data-theme="light"] .markdown-body .chroma .g { color: #dc2626; } + html[data-theme="light"] .markdown-body .chroma .gd { color: #dc2626; } + html[data-theme="light"] .markdown-body .chroma .ge { font-style: italic; } + html[data-theme="light"] .markdown-body .chroma .gi { color: #16a34a; } + html[data-theme="light"] .markdown-body .chroma .gs { font-weight: bold; } + + + .mb-codeblock { + margin: 1.75rem 0; + border: 1px solid color-mix(in srgb, var(--color-border) 65%, transparent); + border-radius: 0.85rem; + overflow: hidden; + background: color-mix(in srgb, var(--color-surface) 35%, transparent); + box-shadow: + 0 4px 6px rgba(0, 0, 0, 0.1), + 0 10px 20px rgba(0, 0, 0, 0.15); + transition: box-shadow 0.2s ease-out; + } + + .mb-codeblock:hover { + box-shadow: + 0 4px 6px rgba(0, 0, 0, 0.15), + 0 12px 24px rgba(0, 0, 0, 0.2); + } + + html[data-theme="light"] .mb-codeblock { + background: #f9fafb; + border-color: #e5e7eb; + box-shadow: + 0 2px 4px rgba(0, 0, 0, 0.05), + 0 4px 8px rgba(0, 0, 0, 0.08); + } + + html[data-theme="light"] .mb-codeblock:hover { + box-shadow: + 0 4px 8px rgba(0, 0, 0, 0.08), + 0 8px 16px rgba(0, 0, 0, 0.12); + } + + .mb-codeblock-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 0.7rem 1rem; + border-bottom: 1px solid color-mix(in srgb, var(--color-border) 55%, transparent); + background: color-mix(in srgb, var(--color-bg) 45%, transparent); + gap: 1rem; + } + + html[data-theme="light"] .mb-codeblock-header { + background: #f3f4f6; + border-bottom-color: #e5e7eb; + } + + .mb-codeblock-left { + display: flex; + align-items: center; + gap: 0.6rem; + min-width: 0; + flex: 1; + } + + .mb-codeblock-badge { + font-size: 0.68rem; + font-weight: 700; + padding: 0.25rem 0.55rem; + border-radius: 0.35rem; + background: color-mix(in srgb, var(--color-accent) 22%, transparent); + color: var(--color-accent); + text-transform: uppercase; + letter-spacing: 0.06em; + white-space: nowrap; + border: 1px solid color-mix(in srgb, var(--color-accent) 35%, transparent); + } + + .mb-codeblock-filename { + display: inline-flex; + align-items: center; + gap: 0.4rem; + color: var(--color-text); + font-size: 0.75rem; + font-weight: 500; + padding: 0.2rem 0.65rem; + border-radius: 0.35rem; + background: color-mix(in srgb, var(--color-bg) 35%, transparent); + border: 1px solid color-mix(in srgb, var(--color-border) 60%, transparent); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + max-width: 20rem; + } + + .mb-codeblock-actions { + display: flex; + gap: 0.4rem; + flex-shrink: 0; + } + + .mb-action-btn { + display: inline-flex; + align-items: center; + gap: 0.35rem; + background: transparent; + border: 1px solid color-mix(in srgb, var(--color-border) 70%, transparent); + color: var(--color-text-muted); + cursor: pointer; + font-size: 0.7rem; + font-weight: 500; + padding: 0.35rem 0.7rem; + border-radius: 0.4rem; + transition: all 0.15s ease-out; + font-family: inherit; + white-space: nowrap; + } + + .mb-action-btn:hover { + color: var(--color-accent); + background: color-mix(in srgb, var(--color-accent) 12%, transparent); + border-color: var(--color-accent); + transform: translateY(-1px); + box-shadow: 0 2px 8px rgba(168, 85, 247, 0.2); + } + + .mb-action-btn:active { + transform: translateY(0); + } + + .mb-action-btn i { + font-size: 0.7rem; + } + + .mb-btn-success { + color: #22c55e !important; + border-color: #22c55e !important; + background: color-mix(in srgb, #22c55e 15%, transparent) !important; + } + + .mb-codeblock-content { + position: relative; + background: color-mix(in srgb, var(--color-bg) 20%, transparent); + transition: max-height 0.3s ease-out; + } + + html[data-theme="light"] .mb-codeblock-content { + background: #ffffff; + } + + .mb-codeblock-content pre { + margin: 0; + padding: 1.25rem 1.5rem; + overflow-x: auto; + background: transparent; + border: none; + } + + .mb-codeblock-content pre code { + font-family: 'JetBrains Mono', 'Fira Code', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; + font-size: 0.88rem; + line-height: 1.65; + font-weight: 400; + } + + /* Custom scrollbar for code blocks */ + .mb-codeblock-content pre::-webkit-scrollbar { + height: 8px; + } + + .mb-codeblock-content pre::-webkit-scrollbar-track { + background: color-mix(in srgb, var(--color-bg) 30%, transparent); + border-radius: 4px; + } + + .mb-codeblock-content pre::-webkit-scrollbar-thumb { + background: color-mix(in srgb, var(--color-border) 70%, transparent); + border-radius: 4px; + } + + .mb-codeblock-content pre::-webkit-scrollbar-thumb:hover { + background: var(--color-accent); + } + + /* Collapse overlay */ + .mb-collapse-overlay { + display: none; + position: absolute; + inset: 0; + background: linear-gradient( + to bottom, + transparent 0%, + rgba(0, 0, 0, 0.25) 35%, + rgba(0, 0, 0, 0.88) 100% + ); + align-items: flex-end; + justify-content: center; + padding-bottom: 1.2rem; + cursor: pointer; + z-index: 10; + backdrop-filter: blur(1px); + } + + .mb-expand-trigger { + display: inline-flex; + align-items: center; + gap: 0.45rem; + padding: 0.5rem 1rem; + border-radius: 0.5rem; + border: 1px solid var(--color-accent); + background: color-mix(in srgb, var(--color-accent) 18%, transparent); + color: var(--color-accent); + font-size: 0.75rem; + font-weight: 650; + cursor: pointer; + transition: all 0.15s ease-out; + backdrop-filter: blur(10px); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); + } + + .mb-expand-trigger:hover { + background: color-mix(in srgb, var(--color-accent) 28%, transparent); + transform: translateY(-2px); + box-shadow: 0 6px 16px rgba(168, 85, 247, 0.35); + border-color: color-mix(in srgb, var(--color-accent) 90%, white); + } + + .mb-expand-trigger i { + font-size: 0.7rem; + animation: bounce-icon 1.8s ease-in-out infinite; + } + + @keyframes bounce-icon { + 0%, 100% { + transform: translateY(0); + } + 50% { + transform: translateY(4px); + } + } + + .mb-codeblock[data-lang="javascript"] .mb-codeblock-badge, + .mb-codeblock[data-lang="js"] .mb-codeblock-badge { + background: color-mix(in srgb, #f7df1e 28%, transparent); + color: #f7df1e; + border-color: color-mix(in srgb, #f7df1e 40%, transparent); + } + + .mb-codeblock[data-lang="typescript"] .mb-codeblock-badge, + .mb-codeblock[data-lang="ts"] .mb-codeblock-badge { + background: color-mix(in srgb, #3178c6 28%, transparent); + color: #60a5fa; + border-color: color-mix(in srgb, #3178c6 40%, transparent); + } + + .mb-codeblock[data-lang="python"] .mb-codeblock-badge, + .mb-codeblock[data-lang="py"] .mb-codeblock-badge { + background: color-mix(in srgb, #3776ab 28%, transparent); + color: #60a5fa; + border-color: color-mix(in srgb, #3776ab 40%, transparent); + } + + .mb-codeblock[data-lang="go"] .mb-codeblock-badge { + background: color-mix(in srgb, #00add8 28%, transparent); + color: #22d3ee; + border-color: color-mix(in srgb, #00add8 40%, transparent); + } + + .mb-codeblock[data-lang="rust"] .mb-codeblock-badge, + .mb-codeblock[data-lang="rs"] .mb-codeblock-badge { + background: color-mix(in srgb, #ce422b 28%, transparent); + color: #fb923c; + border-color: color-mix(in srgb, #ce422b 40%, transparent); + } + + .mb-codeblock[data-lang="html"] .mb-codeblock-badge { + background: color-mix(in srgb, #e34c26 28%, transparent); + color: #f87171; + border-color: color-mix(in srgb, #e34c26 40%, transparent); + } + + .mb-codeblock[data-lang="css"] .mb-codeblock-badge { + background: color-mix(in srgb, #264de4 28%, transparent); + color: #60a5fa; + border-color: color-mix(in srgb, #264de4 40%, transparent); + } + + .mb-codeblock[data-lang="bash"] .mb-codeblock-badge, + .mb-codeblock[data-lang="sh"] .mb-codeblock-badge, + .mb-codeblock[data-lang="shell"] .mb-codeblock-badge { + background: color-mix(in srgb, #4eaa25 28%, transparent); + color: #86efac; + border-color: color-mix(in srgb, #4eaa25 40%, transparent); + } + + .mb-codeblock[data-lang="json"] .mb-codeblock-badge { + background: color-mix(in srgb, #888888 28%, transparent); + color: #d1d5db; + border-color: color-mix(in srgb, #888888 40%, transparent); + } + + .mb-codeblock[data-lang="yaml"] .mb-codeblock-badge, + .mb-codeblock[data-lang="yml"] .mb-codeblock-badge { + background: color-mix(in srgb, #cb171e 28%, transparent); + color: #fca5a5; + border-color: color-mix(in srgb, #cb171e 40%, transparent); + } + + .mb-codeblock[data-lang="java"] .mb-codeblock-badge { + background: color-mix(in srgb, #f89820 28%, transparent); + color: #fbbf24; + border-color: color-mix(in srgb, #f89820 40%, transparent); + } + + .mb-codeblock[data-lang="cpp"] .mb-codeblock-badge, + .mb-codeblock[data-lang="c++"] .mb-codeblock-badge { + background: color-mix(in srgb, #00599c 28%, transparent); + color: #60a5fa; + border-color: color-mix(in srgb, #00599c 40%, transparent); + } + + .mb-codeblock[data-lang="c"] .mb-codeblock-badge { + background: color-mix(in srgb, #555555 28%, transparent); + color: #9ca3af; + border-color: color-mix(in srgb, #555555 40%, transparent); + } + + .mb-codeblock[data-lang="ruby"] .mb-codeblock-badge, + .mb-codeblock[data-lang="rb"] .mb-codeblock-badge { + background: color-mix(in srgb, #cc342d 28%, transparent); + color: #f87171; + border-color: color-mix(in srgb, #cc342d 40%, transparent); + } + + .mb-codeblock[data-lang="php"] .mb-codeblock-badge { + background: color-mix(in srgb, #777bb4 28%, transparent); + color: #a78bfa; + border-color: color-mix(in srgb, #777bb4 40%, transparent); + } + + .mb-codeblock[data-lang="sql"] .mb-codeblock-badge { + background: color-mix(in srgb, #e38c00 28%, transparent); + color: #fbbf24; + border-color: color-mix(in srgb, #e38c00 40%, transparent); + } + + /* Responsive adjustments */ + @media (max-width: 640px) { + .mb-codeblock-header { + flex-direction: column; + align-items: flex-start; + gap: 0.6rem; + } + + .mb-codeblock-actions { + width: 100%; + justify-content: flex-end; + } + + .mb-action-btn span { + display: none; + } + + .mb-action-btn { + padding: 0.4rem; + width: 2rem; + height: 2rem; + justify-content: center; + } + + .mb-codeblock-filename { + max-width: 100%; + } + } + + @keyframes tech-marquee { + 0% { + transform: translateX(0); + } + 100% { + transform: translateX(-50%); + } + } + + @media (prefers-reduced-motion: reduce) { + .tech-strip-track { + animation: none; + } + } + + /* gallery */ + .markdown-body .gallery-container { + margin: 2rem 0; + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: 1rem; + } + + @media (max-width: 640px) { + .markdown-body .gallery-container { + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 0.75rem; + } + } + + .markdown-body .gallery-container a { + display: block; + border: none !important; + padding: 0; + margin: 0; + border-radius: 0.5rem; + overflow: hidden; + transition: all 0.2s ease-out; + position: relative; + cursor: zoom-in; + aspect-ratio: 4/3; + } + + .markdown-body .gallery-container a:hover { + transform: translateY(-2px); + box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2); + } + + .markdown-body .gallery-container img { + display: block; + width: 100%; + height: 100%; + object-fit: cover; + margin: 0; + border: none; + border-radius: 0.5rem; + transition: all 0.2s ease-out; + } + + .markdown-body .gallery-container a:hover img { + filter: brightness(0.9); + } + + /* ==================== */ + /* ABOUT PAGE */ + /* ==================== */ + diff --git a/themes/minimal-black/assets/css/content/toc.css b/themes/minimal-black/assets/css/content/toc.css new file mode 100644 index 0000000..deb3284 --- /dev/null +++ b/themes/minimal-black/assets/css/content/toc.css @@ -0,0 +1,211 @@ +/* ========================================================================== + TABLE OF CONTENTS + TOC sidebar, active link tracking, responsive behavior + ========================================================================== */ + + .article-layout { + display: grid; + grid-template-columns: 1fr; + gap: 2rem; + position: relative; + min-width: 0; + } + + @media (min-width: 1024px) { + .article-layout { + grid-template-columns: 260px 1fr; + gap: 3rem; + } + + .article-toc { + order: 1; + } + + .article-main { + order: 2; + } + } + + .article-main { + min-width: 0; + overflow-x: hidden; + } + + .article-toc { + width: 100%; + } + + .toc-wrapper { + position: sticky; + top: 2rem; + background: color-mix(in srgb, var(--color-surface) 50%, transparent); + border: 1px solid var(--color-border); + border-radius: 0.85rem; + padding: 1rem; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); + backdrop-filter: blur(10px); + transition: all 0.2s ease-out; + max-height: calc(100vh - 24rem); + overflow: hidden; + display: flex; + flex-direction: column; + } + + .toc-wrapper:hover { + box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2); + border-color: color-mix(in srgb, var(--color-accent) 40%, transparent); + } + + .toc-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 0.75rem; + padding-bottom: 0.75rem; + border-bottom: 1px solid color-mix(in srgb, var(--color-border) 60%, transparent); + } + + .toc-title { + display: flex; + align-items: center; + gap: 0.5rem; + font-size: 0.85rem; + font-weight: 650; + color: var(--color-text); + margin: 0; + } + + .toc-title i { + color: var(--color-accent); + font-size: 0.75rem; + } + + .toc-toggle { + display: flex; + background: transparent; + border: 1px solid var(--color-border); + color: var(--color-text-muted); + width: 1.75rem; + height: 1.75rem; + border-radius: 0.4rem; + cursor: pointer; + transition: all 0.15s ease-out; + align-items: center; + justify-content: center; + } + + .toc-toggle:hover { + color: var(--color-accent); + border-color: var(--color-accent); + background: color-mix(in srgb, var(--color-accent) 10%, transparent); + } + + .toc-toggle i { + font-size: 0.7rem; + transition: transform 0.2s ease-out; + } + + .toc-wrapper.collapsed .toc-toggle i { + transform: rotate(-90deg); + } + + .toc-wrapper.collapsed .toc-nav { + max-height: 0; + opacity: 0; + overflow: hidden; + } + + @media (max-width: 1023px) { + .toc-wrapper { + position: relative; + top: 0; + } + } + + .toc-nav { + font-size: 0.8rem; + line-height: 1.6; + transition: all 0.3s ease-out; + overflow-y: auto; + overflow-x: hidden; + flex: 1; + min-height: 0; + } + + /* Custom scrollbar for TOC */ + .toc-nav::-webkit-scrollbar { + width: 6px; + } + + .toc-nav::-webkit-scrollbar-track { + background: color-mix(in srgb, var(--color-bg) 30%, transparent); + border-radius: 3px; + } + + .toc-nav::-webkit-scrollbar-thumb { + background: color-mix(in srgb, var(--color-border) 70%, transparent); + border-radius: 3px; + } + + .toc-nav::-webkit-scrollbar-thumb:hover { + background: var(--color-accent); + } + + .toc-nav > ul { + list-style: none; + padding-left: 0; + margin: 0; + } + + .toc-nav ul { + list-style: none; + margin: 0; + } + + .toc-nav ul ul { + padding-left: 1rem; + margin-top: 0.25rem; + border-left: 1px solid color-mix(in srgb, var(--color-border) 50%, transparent); + } + + .toc-nav li { + margin: 0.35rem 0; + padding-left: 0; + } + + .toc-nav li:hover { + background: none; + } + + .toc-nav a { + display: block; + padding: 0.35rem 0.5rem; + color: var(--color-text-muted); + text-decoration: none; + border-left: 2px solid transparent; + border-radius: 0.35rem; + transition: all 0.15s ease-out; + border-bottom: none; + } + + .toc-nav a:hover { + color: var(--color-text); + background: color-mix(in srgb, var(--color-surface) 40%, transparent); + border-left-color: var(--color-accent); + transform: translateX(3px); + } + + .toc-nav a.active { + color: var(--color-accent); + background: color-mix(in srgb, var(--color-accent) 12%, transparent); + border-left-color: var(--color-accent); + font-weight: 600; + } + + /* Mobile: TOC appears at top */ + @media (max-width: 1023px) { + .article-toc { + order: -1; + margin-bottom: 1.5rem; + } + } \ No newline at end of file diff --git a/themes/minimal-black/assets/css/main.css b/themes/minimal-black/assets/css/main.css new file mode 100644 index 0000000..6e2e8b0 --- /dev/null +++ b/themes/minimal-black/assets/css/main.css @@ -0,0 +1,28 @@ +/* ========================================================================== + MINIMAL BLACK THEME - MAIN STYLESHEET + A minimal, dark-mode first Hugo theme + ========================================================================== */ + +/* Base Styles & Tailwind */ +@import "base.css"; + +/* Utility Classes */ +@import "utilities.css"; + +/* Components */ +@import "components/dock.css"; +@import "components/cards.css"; +@import "components/navigation.css"; +@import "components/search.css"; +@import "components/tech-marquee.css"; + +/* Content */ +@import "content/markdown.css"; +@import "content/toc.css"; + +/* Pages */ +@import "pages/about.css"; +@import "pages/about-alternative.css"; + +/* Responsive Styles */ +@import "responsive.css"; diff --git a/themes/minimal-black/assets/css/pages/about-alternative.css b/themes/minimal-black/assets/css/pages/about-alternative.css new file mode 100644 index 0000000..0cac851 --- /dev/null +++ b/themes/minimal-black/assets/css/pages/about-alternative.css @@ -0,0 +1,363 @@ +/* ========================================================================== + ABOUT ALTERNATIVE PAGE STYLES + Alternative about page with sidebar profile card + ========================================================================== */ + + .page-int, .about-alt-page { + max-width: 1200px; + margin-inline: auto; + } + + .about-alt-layout { + display: grid; + grid-template-columns: 1fr; + gap: 2rem; + padding: 2rem 0; + } + + @media (min-width: 1024px) { + .about-alt-layout { + grid-template-columns: 320px 1fr; + gap: 3rem; + } + } + + /* Profile Card - Left Sidebar */ + .about-alt-sidebar { + position: relative; + } + + .about-alt-profile-card { + background: linear-gradient( + 135deg, + color-mix(in srgb, var(--color-surface) 70%, transparent), + color-mix(in srgb, var(--color-surface) 50%, transparent) + ); + border: 1px solid color-mix(in srgb, var(--color-border) 60%, transparent); + border-radius: 1.5rem; + padding: 2rem; + text-align: center; + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2); + position: sticky; + top: 2rem; + } + + .about-alt-avatar, + .about-alt-avatar-placeholder { + width: 120px; + height: 120px; + border-radius: 50%; + margin: 0 auto 1.5rem; + overflow: hidden; + border: 4px solid var(--color-accent); + box-shadow: 0 0 0 8px color-mix(in srgb, var(--color-accent) 15%, transparent); + transition: all 0.3s ease-out; + } + + .about-alt-avatar:hover, + .about-alt-avatar-placeholder:hover { + transform: scale(1.05); + box-shadow: 0 0 0 12px color-mix(in srgb, var(--color-accent) 20%, transparent); + } + + .about-alt-avatar img { + width: 100%; + height: 100%; + object-fit: cover; + } + + .about-alt-avatar-placeholder { + display: flex; + align-items: center; + justify-content: center; + background: linear-gradient( + 135deg, + color-mix(in srgb, var(--color-accent) 30%, transparent), + color-mix(in srgb, var(--color-accent) 15%, transparent) + ); + } + + .about-alt-avatar-placeholder i { + font-size: 3rem; + color: var(--color-accent); + } + + .about-alt-name { + font-size: 1.75rem; + font-weight: 700; + color: var(--color-text); + margin-bottom: 0.5rem; + } + + .about-alt-role { + font-size: 0.9rem; + color: var(--color-text-muted); + line-height: 1.5; + margin-bottom: 1rem; + } + + .about-alt-meta { + display: flex; + align-items: center; + justify-content: center; + gap: 0.5rem; + font-size: 0.85rem; + color: var(--color-text-muted); + margin-bottom: 1.5rem; + } + + .about-alt-meta i { + color: var(--color-accent); + } + + /* Stats Grid */ + .about-alt-stats { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 1rem; + margin: 2rem 0; + padding: 1.5rem 0; + border-top: 1px solid color-mix(in srgb, var(--color-border) 50%, transparent); + border-bottom: 1px solid color-mix(in srgb, var(--color-border) 50%, transparent); + } + + .about-alt-stat { + text-align: center; + } + + .about-alt-stat-value { + font-size: 1.75rem; + font-weight: 700; + color: var(--color-accent); + margin-bottom: 0.25rem; + } + + .about-alt-stat-label { + font-size: 0.7rem; + color: var(--color-text-muted); + text-transform: uppercase; + letter-spacing: 0.05em; + } + + /* Social Icons */ + .about-alt-social { + display: flex; + justify-content: center; + gap: 0.75rem; + margin-top: 1.5rem; + } + + .about-alt-social-icon { + display: flex; + align-items: center; + justify-content: center; + width: 2.5rem; + height: 2.5rem; + border-radius: 50%; + background: color-mix(in srgb, var(--color-bg) 50%, transparent); + border: 1px solid color-mix(in srgb, var(--color-border) 60%, transparent); + color: var(--color-text-muted); + transition: all 0.2s ease-out; + } + + .about-alt-social-icon:hover { + background: var(--color-accent); + border-color: var(--color-accent); + color: white; + transform: translateY(-3px); + box-shadow: 0 6px 16px rgba(0, 0, 0, 0.3); + } + + .about-alt-social-icon i { + font-size: 1rem; + } + + /* Content Area */ + .about-alt-content { + display: flex; + flex-direction: column; + gap: 2rem; + } + + .about-alt-section { + background: color-mix(in srgb, var(--color-surface) 40%, transparent); + border: 1px solid color-mix(in srgb, var(--color-border) 60%, transparent); + border-radius: 1.25rem; + padding: 2rem; + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15); + } + + .about-alt-section-title { + display: flex; + align-items: center; + gap: 0.75rem; + font-size: 1.5rem; + font-weight: 700; + color: var(--color-text); + margin-bottom: 1.5rem; + padding-bottom: 1rem; + border-bottom: 2px solid color-mix(in srgb, var(--color-accent) 20%, transparent); + } + + .about-alt-section-title i { + color: var(--color-accent); + font-size: 1.25rem; + } + + /* Experience Cards Grid */ + .about-alt-experience-grid { + display: grid; + gap: 1.5rem; + } + + .about-alt-experience-card { + background: color-mix(in srgb, var(--color-bg) 40%, transparent); + border: 1px solid color-mix(in srgb, var(--color-border) 50%, transparent); + border-left: 4px solid var(--color-accent); + border-radius: 0.85rem; + padding: 1.5rem; + transition: all 0.3s ease-out; + position: relative; + overflow: hidden; + } + + .about-alt-experience-card::before { + content: ''; + position: absolute; + top: 0; + right: 0; + width: 100px; + height: 100px; + background: radial-gradient( + circle at center, + color-mix(in srgb, var(--color-accent) 10%, transparent), + transparent 70% + ); + opacity: 0; + transition: opacity 0.3s ease-out; + } + + .about-alt-experience-card:hover { + border-left-width: 6px; + transform: translateX(6px); + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.25); + } + + .about-alt-experience-card:hover::before { + opacity: 1; + } + + .about-alt-experience-card p:first-child { + margin-top: 0; + } + + .about-alt-experience-card p:last-child { + margin-bottom: 0; + } + + .about-alt-experience-card strong { + font-size: 1.1rem; + color: var(--color-text); + display: block; + margin-bottom: 0.25rem; + } + + .about-alt-experience-card em { + font-size: 0.85rem; + color: var(--color-text-muted); + font-style: normal; + display: block; + margin-bottom: 0.75rem; + } + + /* Skills Badge Cloud */ + .about-alt-skills { + display: flex; + flex-wrap: wrap; + gap: 0.75rem; + } + + .about-alt-skill { + display: inline-flex; + align-items: center; + gap: 0.5rem; + padding: 0.5rem 1rem; + background: color-mix(in srgb, var(--color-bg) 50%, transparent); + border: 1px solid color-mix(in srgb, var(--color-border) 60%, transparent); + border-radius: 999px; + font-size: 0.85rem; + font-weight: 500; + color: var(--color-text); + transition: all 0.2s ease-out; + cursor: default; + } + + .about-alt-skill i { + font-size: 1.1em; + opacity: 0.9; + } + + .about-alt-skill:hover { + background: var(--color-accent); + border-color: var(--color-accent); + color: white; + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); + } + + .about-alt-skill:hover i { + opacity: 1; + } + + /* Responsive */ + @media (max-width: 1023px) { + .about-alt-profile-card { + position: relative; + top: 0; + } + + .about-alt-layout { + grid-template-columns: 1fr; + } + } + + @media (max-width: 640px) { + .about-alt-profile-card { + padding: 1.5rem; + } + + .about-alt-avatar, + .about-alt-avatar-placeholder { + width: 100px; + height: 100px; + } + + .about-alt-name { + font-size: 1.5rem; + } + + .about-alt-stats { + gap: 0.75rem; + } + + .about-alt-stat-value { + font-size: 1.5rem; + } + + .about-alt-section { + padding: 1.5rem; + } + + .about-alt-section-title { + font-size: 1.25rem; + } + + .about-alt-experience-card { + padding: 1.25rem; + } + } + + /* ==================== */ + /* TABLE OF CONTENTS */ + /* ==================== */ + diff --git a/themes/minimal-black/assets/css/pages/about.css b/themes/minimal-black/assets/css/pages/about.css new file mode 100644 index 0000000..ff516fd --- /dev/null +++ b/themes/minimal-black/assets/css/pages/about.css @@ -0,0 +1,380 @@ +/* ========================================================================== + ABOUT PAGE STYLES + Standard about page with timeline + ========================================================================== */ + + .about-page { + max-width: 900px; + margin-inline: auto; + } + + .about-hero { + text-align: center; + padding: 3rem 0 4rem; + position: relative; + } + + .about-hero::after { + content: ''; + position: absolute; + bottom: 0; + left: 50%; + transform: translateX(-50%); + width: 60px; + height: 3px; + background: linear-gradient( + 90deg, + transparent, + var(--color-accent), + transparent + ); + border-radius: 999px; + } + + .about-hero-content { + display: flex; + flex-direction: column; + align-items: center; + gap: 1.5rem; + } + + .about-avatar, + .about-avatar-placeholder { + width: 140px; + height: 140px; + border-radius: 50%; + overflow: hidden; + border: 3px solid var(--color-accent); + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2), + 0 0 0 8px color-mix(in srgb, var(--color-accent) 15%, transparent); + transition: all 0.3s ease-out; + } + + .about-avatar:hover, + .about-avatar-placeholder:hover { + transform: translateY(-4px) scale(1.02); + box-shadow: 0 12px 32px rgba(0, 0, 0, 0.3), + 0 0 0 12px color-mix(in srgb, var(--color-accent) 20%, transparent); + } + + .about-avatar img { + width: 100%; + height: 100%; + object-fit: cover; + } + + .about-avatar-placeholder { + display: flex; + align-items: center; + justify-content: center; + background: linear-gradient( + 135deg, + color-mix(in srgb, var(--color-accent) 20%, transparent), + color-mix(in srgb, var(--color-accent) 10%, transparent) + ); + backdrop-filter: blur(10px); + } + + .about-avatar-placeholder i { + font-size: 4rem; + color: var(--color-accent); + } + + .about-title { + font-size: 2.5rem; + font-weight: 700; + letter-spacing: -0.02em; + color: var(--color-text); + margin: 0; + line-height: 1.2; + } + + .about-subtitle { + font-size: 1.1rem; + color: var(--color-text-muted); + max-width: 600px; + line-height: 1.6; + margin: 0; + } + + .about-content { + margin-bottom: 4rem; + } + + .about-content .card { + background: color-mix(in srgb, var(--color-surface) 60%, transparent); + border: 1px solid color-mix(in srgb, var(--color-border) 70%, transparent); + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12); + padding: 2rem; + } + + .about-content .markdown-body h3 { + display: flex; + align-items: center; + gap: 0.75rem; + margin-top: 2.5rem; + padding-bottom: 0.5rem; + border-bottom: 2px solid color-mix(in srgb, var(--color-accent) 30%, transparent); + } + + .about-content .markdown-body h3::before { + content: ''; + display: inline-block; + width: 4px; + height: 1.5rem; + background: var(--color-accent); + border-radius: 999px; + } + + /* Timeline */ + .timeline { + position: relative; + padding: 2rem 0 1rem 0; + margin-top: 2rem; + } + + .timeline::before { + content: ''; + position: absolute; + left: 20px; + top: 0; + bottom: 0; + width: 2px; + background: linear-gradient( + to bottom, + transparent, + var(--color-accent) 10%, + var(--color-accent) 90%, + transparent + ); + } + + .timeline-item { + position: relative; + padding-left: 60px; + margin-bottom: 3rem; + } + + .timeline-item:last-child { + margin-bottom: 0; + } + + .timeline-marker { + position: absolute; + left: 0; + top: 8px; + width: 42px; + height: 42px; + display: flex; + align-items: center; + justify-content: center; + z-index: 2; + } + + .timeline-marker::before { + content: ''; + position: absolute; + width: 16px; + height: 16px; + background: var(--color-accent); + border-radius: 50%; + border: 3px solid var(--color-bg); + box-shadow: + 0 0 0 4px color-mix(in srgb, var(--color-accent) 20%, transparent), + 0 4px 12px rgba(0, 0, 0, 0.3); + transition: all 0.3s ease-out; + } + + .timeline-item:hover .timeline-marker::before { + transform: scale(1.3); + box-shadow: + 0 0 0 6px color-mix(in srgb, var(--color-accent) 30%, transparent), + 0 6px 16px rgba(0, 0, 0, 0.4); + } + + .timeline-content { + background: color-mix(in srgb, var(--color-surface) 30%, transparent); + border: 1px solid color-mix(in srgb, var(--color-border) 50%, transparent); + border-radius: 0.85rem; + padding: 1.5rem; + transition: all 0.3s ease-out; + position: relative; + overflow: hidden; + } + + .timeline-content::before { + content: ''; + position: absolute; + left: 0; + top: 0; + bottom: 0; + width: 4px; + background: var(--color-accent); + opacity: 0; + transition: opacity 0.3s ease-out; + } + + .timeline-item:hover .timeline-content { + background: color-mix(in srgb, var(--color-surface) 50%, transparent); + border-color: color-mix(in srgb, var(--color-accent) 40%, transparent); + transform: translateX(4px); + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2); + } + + .timeline-item:hover .timeline-content::before { + opacity: 1; + } + + .timeline-content > p:first-child { + margin-top: 0; + } + + .timeline-content > p:last-child { + margin-bottom: 0; + } + + .timeline-content strong { + font-size: 1.1rem; + color: var(--color-text); + display: block; + margin-bottom: 0.25rem; + } + + .timeline-content em { + font-size: 0.85rem; + color: var(--color-text-muted); + font-style: normal; + display: block; + margin-bottom: 0.75rem; + } + + .timeline-content a { + color: var(--color-accent); + } + + /* Remove hr from timeline */ + .timeline hr { + display: none; + } + + .about-social { + text-align: center; + padding: 3rem 2rem; + background: color-mix(in srgb, var(--color-surface) 40%, transparent); + border: 1px solid color-mix(in srgb, var(--color-border) 60%, transparent); + border-radius: 1.25rem; + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1); + } + + .about-social-title { + font-size: 1.25rem; + font-weight: 650; + color: var(--color-text); + margin-bottom: 1.5rem; + } + + .about-social-links { + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 1rem; + } + + .about-social-link { + display: inline-flex; + align-items: center; + gap: 0.5rem; + padding: 0.75rem 1.5rem; + border-radius: 999px; + background: color-mix(in srgb, var(--color-bg) 60%, transparent); + border: 1px solid color-mix(in srgb, var(--color-border) 70%, transparent); + color: var(--color-text-muted); + text-decoration: none; + font-size: 0.9rem; + font-weight: 500; + transition: all 0.2s ease-out; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); + } + + .about-social-link:hover { + transform: translateY(-2px); + background: color-mix(in srgb, var(--color-surface) 80%, transparent); + border-color: var(--color-accent); + color: var(--color-accent); + box-shadow: 0 6px 20px rgba(0, 0, 0, 0.15); + } + + .about-social-link i { + font-size: 1.1rem; + } + + /* Responsive about page */ + @media (max-width: 640px) { + .about-hero { + padding: 2rem 0 3rem; + } + + .about-avatar, + .about-avatar-placeholder { + width: 110px; + height: 110px; + } + + .about-avatar-placeholder i { + font-size: 3rem; + } + + .about-title { + font-size: 2rem; + } + + .about-subtitle { + font-size: 1rem; + } + + .about-social { + padding: 2rem 1.25rem; + } + + .about-social-links { + flex-direction: column; + align-items: stretch; + } + + .about-social-link { + justify-content: center; + } + + /* Timeline responsive */ + .timeline::before { + left: 12px; + } + + .timeline-item { + padding-left: 40px; + } + + .timeline-marker { + left: -5px; + width: 34px; + height: 34px; + } + + .timeline-marker::before { + width: 12px; + height: 12px; + } + + .timeline-content { + padding: 1rem; + } + + .timeline-content strong { + font-size: 1rem; + } + + .timeline-content em { + font-size: 0.8rem; + } + } + diff --git a/themes/minimal-black/assets/css/responsive.css b/themes/minimal-black/assets/css/responsive.css new file mode 100644 index 0000000..a209c7c --- /dev/null +++ b/themes/minimal-black/assets/css/responsive.css @@ -0,0 +1,277 @@ +/* ========================================================================== + RESPONSIVE STYLES + Global responsive breakpoints and mobile optimizations + ========================================================================== */ + + /* Mobile - Small screens (up to 640px) */ + @media (max-width: 640px) { + .card-pad { + padding: 1.25rem; + } + + .layout-page { + padding-inline: 1rem; + padding-block: 1.5rem; + } + + .layout-page-tight { + padding-inline: 1rem; + padding-block: 1rem; + } + + /* Markdown body */ + .markdown-body { + font-size: 0.9rem; + padding: 0; + } + + /* Headings - scale down for mobile */ + .markdown-body h1 { + font-size: 1.75rem; + } + + .markdown-body h2 { + font-size: 1.35rem; + margin-top: 2rem; + } + + .markdown-body h3 { + font-size: 1.15rem; + margin-top: 1.5rem; + } + + .markdown-body h4 { + font-size: 1rem; + } + + .markdown-body h5, + .markdown-body h6 { + font-size: 0.9rem; + } + + /* Heading anchors - adjust for smaller screens */ + .markdown-body .md-heading-anchor::before { + left: -1rem; + font-size: 0.85em; + } + + /* Blockquotes */ + .markdown-body blockquote, + .markdown-body .md-blockquote { + margin: 1.25rem 0; + padding: 1rem; + border-left-width: 3px; + } + + .markdown-body blockquote::before, + .markdown-body .md-blockquote::before { + font-size: 2rem; + left: 0.5rem; + top: 0.25rem; + } + + /* Alerts */ + .markdown-body .md-alert { + padding: 0.85rem 1rem 0.85rem 2.5rem; + } + + .markdown-body .md-alert::before { + left: 0.75rem; + top: 0.95rem; + font-size: 0.95rem; + } + + /* Lists */ + .markdown-body ul, + .markdown-body ol { + padding-left: 1.25rem; + } + + /* Code blocks */ + .markdown-body pre { + margin: 1rem -1rem; + border-radius: 0.5rem; + } + + .markdown-body pre code { + font-size: 0.8rem; + padding: 1rem; + } + + .mb-codeblock { + margin: 1.25rem -1rem; + border-radius: 0.5rem; + } + + .mb-codeblock-content pre { + padding: 1rem; + } + + .mb-codeblock-content pre code { + font-size: 0.8rem; + } + + /* Inline code */ + .markdown-body :not(pre) > code { + font-size: 0.85em; + padding: 0.1rem 0.35rem; + } + + /* Tables - full bleed on mobile */ + .markdown-body table { + font-size: 0.85rem; + } + + .markdown-body th, + .markdown-body td { + padding: 0.5rem 0.65rem; + } + + .markdown-body .table-wrap { + margin: 1.25rem -1rem; + border-radius: 0.5rem; + } + + /* Images */ + .markdown-body img, + .markdown-body .md-image { + margin: 1.25rem 0; + } + + .markdown-body .md-image a { + border-radius: 0.5rem; + } + + .markdown-body .md-image a::after { + font-size: 2rem; + } + + .markdown-body .md-image figcaption { + font-size: 0.82rem; + padding: 0.4rem 0.75rem; + } + + /* Page heading */ + .heading-page { + font-size: 1.75rem !important; + line-height: 1.2; + } + + /* Article header */ + .article-main header { + padding: 0; + } + + /* TOC on mobile */ + .toc-wrapper { + border-radius: 0.75rem; + padding: 0.85rem; + } + + .toc-nav { + font-size: 0.85rem; + } + } + + /* Tablet - Medium screens (641px to 1023px) */ + @media (min-width: 641px) and (max-width: 1023px) { + .article-layout { + display: flex; + flex-wrap: wrap; + } + + .article-main { + max-width: -webkit-fill-available; + } + + .layout-page { + padding-inline: 1.5rem; + padding-block: 2rem; + } + + .markdown-body { + font-size: 0.93rem; + } + + .markdown-body h1 { + font-size: 1.85rem; + } + + .markdown-body h2 { + font-size: 1.4rem; + } + + .markdown-body h3 { + font-size: 1.2rem; + } + } + + /* Larger screens - Adjust max-widths */ + @media (min-width: 1536px) { + .article-layout { + padding-inline: 2rem; + } + } + + /* Ultra-wide screens */ + @media (min-width: 1920px) { + .markdown-body { + font-size: 1rem; + } + } + + /* Touch device improvements */ + @media (hover: none) and (pointer: coarse) { + /* Increase tap targets for touch */ + .toc-nav a { + padding: 0.5rem 0.65rem; + margin: 0.15rem 0; + } + + .toc-toggle { + width: 2.5rem; + height: 2.5rem; + } + + .mb-action-btn { + padding: 0.5rem 0.85rem; + font-size: 0.75rem; + } + + /* Remove hover effects on touch devices */ + .markdown-body li:hover { + background: none; + } + + .card-hover:hover { + transform: none; + box-shadow: none; + } + } + + /* Landscape mobile */ + @media (max-height: 500px) and (orientation: landscape) { + .toc-wrapper { + position: relative; + top: 0; + } + + .layout-page { + padding-block: 1rem; + } + } + +@layer base { + .prose { + color: var(--color-text); + } + + .prose a { + color: var(--color-accent); + text-decoration-color: var(--color-accent); + } + + .prose strong { + color: var(--color-text); + } +} diff --git a/themes/minimal-black/assets/css/utilities.css b/themes/minimal-black/assets/css/utilities.css new file mode 100644 index 0000000..763d8e4 --- /dev/null +++ b/themes/minimal-black/assets/css/utilities.css @@ -0,0 +1,95 @@ +/* ========================================================================== + UTILITY CLASSES + Color utilities, animations, helper classes + ========================================================================== */ + + .bg-bg { + background-color: var(--color-bg); + } + .bg-surface { + background-color: var(--color-surface); + } + + .text-text { + color: var(--color-text); + } + .text-muted { + color: var(--color-text-muted); + } + .text-accent { + color: var(--color-accent); + } + + .border-border { + border-color: var(--color-border); + } + .border-accent { + border-color: var(--color-accent); + } + + .bg-accent { + background-color: var(--color-accent); + } + .underline-accent { + text-decoration-color: var(--color-accent); + } + + /* Simple fade-up animation */ + @keyframes fade-up { + 0% { + opacity: 0; + transform: translateY(8px); + } + 100% { + opacity: 1; + transform: translateY(0); + } + } + + .animate-fade-up { + animation: fade-up 0.45s ease-out both; + } + + @keyframes search-panel-in { + 0% { + opacity: 0; + transform: translate(-50%, -48%) scale(0.97); + } + 100% { + opacity: 1; + transform: translate(-50%, -50%) scale(1); + } + } + + @keyframes search-backdrop-in { + 0% { + opacity: 0; + } + 100% { + opacity: 1; + } + } + + @keyframes search-panel-out { + 0% { + opacity: 1; + transform: translate(-50%, -50%) scale(1); + } + 100% { + opacity: 0; + transform: translate(-50%, -48%) scale(0.97); + } + } + + @keyframes search-backdrop-out { + 0% { + opacity: 1; + } + 100% { + opacity: 0; + } + } + + +@layer components { } + /* Layout helpers */ diff --git a/themes/minimal-black/exampleSite/content/_index.md b/themes/minimal-black/exampleSite/content/_index.md new file mode 100644 index 0000000..e69de29 diff --git a/themes/minimal-black/exampleSite/content/about-alternative/_index.md b/themes/minimal-black/exampleSite/content/about-alternative/_index.md new file mode 100644 index 0000000..b26d19b --- /dev/null +++ b/themes/minimal-black/exampleSite/content/about-alternative/_index.md @@ -0,0 +1,128 @@ ++++ +title = "About Me" +subtitle = "Software Engineer | Full Stack Developer | Open Source Enthusiast" +layout = "about-alternative" ++++ + +I'm a software engineer passionate about building elegant solutions to complex problems. Currently working on modern web applications and exploring the intersection of design, performance, and developer experience. + +## What I Do + +I work across the full stack with expertise in: + +- **Backend Development** — Scalable APIs, microservices, database design +- **Frontend Engineering** — React, Vue, modern JavaScript frameworks +- **Cloud & DevOps** — AWS, Docker, Kubernetes, CI/CD pipelines +- **Open Source** — Contributing to projects and building developer tools + +## Current Projects + +Right now I'm focused on: + +- Building minimal Hugo themes for personal sites +- Exploring dark-mode design patterns and accessibility +- Writing about web performance and developer experience +- Contributing to open-source projects in the web ecosystem + +## About This Layout + +> This page demonstrates the **alternative about layout** with a sidebar profile card. + +### Key Features + +This layout includes: + +1. **Left Sidebar Profile Card** with: + - Avatar/profile image (or placeholder icon) + - Name and role + - Location indicator + - Customizable stats (configured in `hugo.toml`) + - Social media links + +2. **Main Content Area** with: + - Introduction section + - Experience cards (from `---` separators) + - Tech stack badges (configured in `hugo.toml`) + +### How to Configure + +**Stats and Skills** are parametrized in your `hugo.toml`: + +```toml +[params.about.alt] + # Custom stats + [[params.about.alt.stats]] + value = "5+" + label = "Years Coding" + + # Tech stack with icons + [[params.about.alt.skills]] + label = "JavaScript" + icon = "devicon-javascript-plain" +``` + +This makes it easy to update your stats and skills without editing this page! + +--- + +**Lead Developer** — [Modern Tech Co](https://example.com) +*2022 – Present • Remote* + +Leading development of cloud-native applications and mentoring engineering teams. Focus on scalable architecture, clean code practices, and continuous delivery. + +--- + +**Senior Engineer** — Startup Ventures +*2020 – 2022 • San Francisco* + +Built full-stack applications from scratch. Worked with React, Node.js, PostgreSQL, and AWS to deliver features serving hundreds of thousands of users. + +--- + +**Software Engineer** — Digital Solutions +*2018 – 2020 • New York* + +Developed enterprise applications using Java, Spring Boot, and modern frontend frameworks. Collaborated with cross-functional teams to ship quality software. + +--- + +**Junior Developer** — Tech Academy +*2016 – 2018 • Boston* + +Started my career building web applications and learning industry best practices. Contributed to client projects and internal tooling. + +--- + +## Layout Comparison + +| Feature | Standard About | Alternative About | +|---------|---------------|-------------------| +| Layout | Centered, single column | Sidebar + content | +| Profile Info | Top hero section | Left sidebar card | +| Stats | Not included | Configurable stats grid | +| Tech Stack | Not included | Icon badges | +| Timeline | Vertical with markers | Card-based grid | +| Best For | Traditional resumes | Modern portfolios | + +Try both layouts to see which fits your style! Switch by changing `layout = "about"` or `layout = "about-alternative"` in the frontmatter. + +### Responsive Design + +Both layouts are fully responsive: +- **Desktop:** Sidebar + content (alternative) or centered (standard) +- **Tablet:** Stacked layout with adjusted spacing +- **Mobile:** Single column, optimized for small screens + +--- + +## Get Started + +To use this layout on your site: + +1. Copy this content file structure +2. Set `layout = "about-alternative"` in frontmatter +3. Configure stats and skills in `hugo.toml` +4. Add your own content and experience +5. Optionally add an avatar image to `static/images/` + +That's it! The theme handles all the styling and responsive behavior automatically. diff --git a/themes/minimal-black/exampleSite/content/about/_index.md b/themes/minimal-black/exampleSite/content/about/_index.md new file mode 100644 index 0000000..315cd6c --- /dev/null +++ b/themes/minimal-black/exampleSite/content/about/_index.md @@ -0,0 +1,111 @@ ++++ +title = "About Me" +subtitle = "Software Engineer | Full Stack Developer | Open Source Enthusiast" +layout = "about" ++++ + +I'm a passionate software engineer with expertise in building scalable web applications and developer tools. With a focus on clean code, performance optimization, and user experience, I help teams ship products that users love. + +## What I Do + +I specialize in full-stack development with a keen interest in: + +- **Backend Engineering** — Building robust APIs and microservices with modern frameworks +- **Frontend Development** — Creating responsive, accessible user interfaces +- **DevOps & Infrastructure** — Container orchestration, CI/CD, and cloud platforms +- **Open Source** — Contributing to and maintaining open-source projects + +## Timeline Demonstration + +> This page demonstrates the **standard about layout** with a timeline feature. Use horizontal rules (`---`) to separate timeline entries. Each section becomes a card in the timeline visualization. + +### Current Focus + +Right now, I'm exploring minimal design patterns, modern web frameworks, and building tools that make developers' lives easier. I believe in writing code that is simple, maintainable, and well-documented. + +--- + +**Senior Software Engineer** — [Tech Company](https://example.com) +*January 2022 – Present • Remote* + +Leading the development of cloud-native applications using modern frameworks and best practices. Mentoring junior developers and driving technical decisions for large-scale projects. + +Key achievements: +- Architected microservices platform serving 1M+ users +- Reduced deployment time by 60% through CI/CD improvements +- Led migration from monolith to distributed architecture + +**Technologies:** React, Node.js, PostgreSQL, Docker, Kubernetes, AWS + +--- + +**Full Stack Developer** — Startup Inc. +*March 2020 – December 2021 • San Francisco, CA* + +Developed and maintained multiple web applications from concept to production. Collaborated with cross-functional teams to deliver features that increased user engagement by 40%. + +- Built RESTful APIs serving 500K+ daily requests +- Implemented real-time features using WebSockets +- Optimized database queries reducing response time by 50% + +**Technologies:** Python, Django, Vue.js, Redis, MySQL, GCP + +--- + +**Software Engineer** — Digital Solutions Corp. +*June 2018 – February 2020 • New York, NY* + +Worked on enterprise solutions for Fortune 500 clients. Focused on backend development, database design, and system integration. + +- Developed internal tools that saved 200+ hours monthly +- Integrated third-party APIs for payment processing +- Maintained legacy systems while modernizing architecture + +**Technologies:** Java, Spring Boot, Oracle DB, Angular, Jenkins + +--- + +**Junior Developer** — Code Academy +*January 2017 – May 2018 • Boston, MA* + +Started my professional journey building web applications and learning industry best practices. Contributed to various client projects and internal tooling. + +- Developed responsive web interfaces +- Wrote automated tests achieving 90%+ coverage +- Participated in code reviews and agile ceremonies + +**Technologies:** JavaScript, PHP, Laravel, MySQL, Git + +--- + +## Education & Certifications + +**Bachelor of Science in Computer Science** +University of Technology • 2013-2017 + +**Certifications:** +- AWS Certified Solutions Architect +- Google Cloud Professional Developer +- Certified Kubernetes Administrator (CKA) + +--- + +## How This Layout Works + +This about page uses the **timeline layout**. Here's how to customize it: + +1. Set `layout = "about"` in the frontmatter +2. Write your introduction before the first `---` separator +3. Each section after `---` becomes a timeline card +4. Use markdown formatting: **bold** for job titles, *italic* for dates +5. Add links using `[text](url)` syntax +6. Timeline items appear in chronological order (newest first) + +### Features Demonstrated + +- ✅ Hero section with avatar and subtitle +- ✅ Timeline visualization with markers and cards +- ✅ Markdown content with formatting +- ✅ Links and emphasis +- ✅ Responsive design +- ✅ Social links in footer diff --git a/themes/minimal-black/exampleSite/content/blog/_index.md b/themes/minimal-black/exampleSite/content/blog/_index.md new file mode 100644 index 0000000..e69de29 diff --git a/themes/minimal-black/exampleSite/content/blog/theme-guide.md b/themes/minimal-black/exampleSite/content/blog/theme-guide.md new file mode 100644 index 0000000..bd6bcfc --- /dev/null +++ b/themes/minimal-black/exampleSite/content/blog/theme-guide.md @@ -0,0 +1,592 @@ ++++ +title = "Complete Guide to Minimal Black Theme" +date = "2025-01-15" +author = "Jim Christopoulos" +tags = ["hugo", "tutorial", "guide", "theme"] +categories = ["documentation"] +description = "A comprehensive guide to all features and capabilities of the Minimal Black Hugo theme" +draft = false ++++ + +Welcome to the complete guide for the Minimal Black Hugo theme! + +This post demonstrates all the features, components, and customization options available in the theme. + +## Introduction + +Minimal Black is designed for developers, designers, and everybody who want a clean, fast, and beautiful personal website. + +This guide will walk you through every feature with examples you can use in your own content. + +## Typography & Text Formatting + +### Headings + +The theme supports all six heading levels with proper hierarchy and spacing: + +# Heading 1 +## Heading 2 +### Heading 3 +#### Heading 4 +##### Heading 5 +###### Heading 6 + +Each heading automatically gets an anchor link (hover to see the # symbol) for easy linking. + +### Text Emphasis + +Use standard Markdown for text formatting: + +- **Bold text** for emphasis +- *Italic text* for subtle emphasis +- ***Bold and italic*** for strong emphasis +- ~~Strikethrough~~ for deleted content +- `Inline code` for code references + +You can also ==highlight text== using the mark syntax (if enabled in your config). + +### Links + +Links are styled with subtle hover effects: + +- [External link](https://example.com) + +## Lists + +### Unordered Lists + +Simple bullet lists: + +- First item +- Second item + - Nested item 1 + - Nested item 2 + - Deeply nested item +- Third item + +### Ordered Lists + +Numbered lists: + +1. First step +2. Second step + 1. Sub-step A + 2. Sub-step B +3. Third step + +### Task Lists + +Interactive checkboxes: + +- [x] Completed task +- [x] Another completed item +- [ ] Pending task +- [ ] Another pending item + - [x] Nested completed + - [ ] Nested pending + +Task lists are great for project planning, feature tracking, or tutorial steps. + +### Definition Lists + +For glossary-style content: + +Hugo +: A fast static site generator written in Go + +Markdown +: A lightweight markup language for formatting text + +Tailwind CSS +: A utility-first CSS framework + +## Blockquotes + +### Standard Blockquotes + +Simple quotes with gradient background: + +> This is a standard blockquote. It has a subtle gradient background and a colored left border for visual distinction. + +> Multi-paragraph blockquotes work too. +> +> They maintain proper spacing between paragraphs while keeping the unified look. + +### Nested Blockquotes + +You can nest quotes: + +> This is the outer quote. +> +> > This is a nested quote inside. +> > +> > > You can nest multiple levels. + +### GitHub-Style Alerts + +The theme supports GitHub-flavored alert callouts: + +> [!NOTE] +> This is a note callout. Use it for informational content that readers should be aware of. + +> [!TIP] +> This is a tip callout. Perfect for helpful suggestions and best practices. + +> [!IMPORTANT] +> This is an important callout. Use it for critical information that must not be missed. + +> [!WARNING] +> This is a warning callout. Great for cautionary information and potential pitfalls. + +> [!CAUTION] +> This is a caution callout. Use for dangerous actions or critical warnings. + +## Code Blocks + +### Inline Code + +Reference code inline with `backticks`. + +Great for mentioning `variables`, `functions()`, or `file-names.txt`. + +### Basic Code Blocks + +Simple code blocks without syntax highlighting: + +``` +This is a plain code block. +No syntax highlighting. +Perfect for plain text or output. +``` + +### Syntax Highlighted Code + +The theme supports syntax highlighting for dozens of languages: + +**JavaScript:** +```javascript + +const greet = (name) => { + console.log(`Hello, ${name}!`); + return `Welcome to the Minimal Black theme`; +}; + +// Async/await example +async function fetchData(url) { + try { + const response = await fetch(url); + const data = await response.json(); + return data; + } catch (error) { + console.error('Error fetching data:', error); + } +} +``` + +**Python:** +```python +def fibonacci(n): + """Calculate fibonacci number recursively.""" + if n <= 1: + return n + return fibonacci(n-1) + fibonacci(n-2) + +# List comprehension example +squares = [x**2 for x in range(10)] +print(squares) +``` + +**Go:** +```go +package main + +import "fmt" + +func main() { + // Simple HTTP server + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, "Hello, World!") + }) + + http.ListenAndServe(":8080", nil) +} +``` + +**Bash:** +```bash +#!/bin/bash + +# Deploy script +hugo --minify +rsync -avz public/ user@server:/var/www/ +echo "Deployment complete!" +``` + +**CSS:** +```css +/* Dark theme variables */ +:root { + --color-bg: #000000; + --color-accent: #c084fc; + --transition: all 0.2s ease; +} + +.button { + background: var(--color-accent); + transition: var(--transition); +} +``` + +**HTML:** +```html + + + + + Minimal Black + + +

Welcome!

+ + +``` + +### Code Block Features + +All code blocks include: + +- **Language Label** — Shows the language in top-right corner +- **Copy Button** — Click to copy code to clipboard +- **Line Numbers** — Optional (configure in hugo.toml) +- **Syntax Highlighting** — Powered by Hugo's Chroma + +## Tables + +### Basic Tables + +| Feature | Supported | Notes | +|---------|-----------|-------| +| Dark Mode | ✅ | True black backgrounds | +| Search | ✅ | Client-side, fast | +| TOC | ✅ | Auto-generated | +| Analytics | ✅ | Multiple providers | + +### Aligned Columns + +| Left Aligned | Center Aligned | Right Aligned | +|:-------------|:--------------:|--------------:| +| Left | Center | Right | +| Text | Text | 100 | +| More | Data | 250 | + +### Complex Tables + +| Language | Supported | Syntax Highlighting | Code Blocks | +|----------|:---------:|:-------------------:|-------------| +| JavaScript | ✅ | ✅ | Yes | +| Python | ✅ | ✅ | Yes | +| Go | ✅ | ✅ | Yes | +| Rust | ✅ | ✅ | Yes | +| TypeScript | ✅ | ✅ | Yes | + +Tables are responsive and scroll horizontally on small screens. + +## Images + +### Basic Image + +![Example image](https://images.pexels.com/photos/546819/pexels-photo-546819.jpeg) + +### Image with Caption + +Images automatically get a lightbox overlay on hover (magnifying glass icon appears). + +### Multiple Images + +![Image 1](https://images.pexels.com/photos/374560/pexels-photo-374560.jpeg) +![Image 2](https://images.pexels.com/photos/1972464/pexels-photo-1972464.jpeg) + +> [!NOTE] +> *Stock Images obtained from pexels.com* + +Images are responsive and scale to fit the content width. + +## Gallery Shortcode + +Use the gallery shortcode for image collections: + +{{< gallery >}} +![Gallery 1](https://images.pexels.com/photos/1972464/pexels-photo-1972464.jpeg) +![Gallery 2](https://images.pexels.com/photos/374560/pexels-photo-374560.jpeg) +![Gallery 3](https://images.pexels.com/photos/546819/pexels-photo-546819.jpeg) +{{< /gallery >}} + +The gallery includes: +- Lightbox functionality +- Click to view full size +- Navigate between images +- Responsive grid layout + +## Mermaid Diagrams + +### Flowchart + +```mermaid +graph TD + A[Start] --> B{Is it dark mode?} + B -->|Yes| C[Load dark theme] + B -->|No| D[Load light theme] + C --> E[Render page] + D --> E + E --> F[User sees beautiful site] +``` + +### Sequence Diagram + +```mermaid +sequenceDiagram + participant User + participant Browser + participant Hugo + participant Theme + + User->>Browser: Visit site + Browser->>Hugo: Request page + Hugo->>Theme: Render template + Theme->>Hugo: Return HTML + Hugo->>Browser: Serve page + Browser->>User: Display site +``` + +### Class Diagram + +```mermaid +classDiagram + class Theme { + +String name + +String version + +render() + +configure() + } + class Config { + +Object params + +validate() + } + class Content { + +String title + +Date date + +render() + } + + Theme --> Config + Theme --> Content +``` + +## Horizontal Rules + +Separate sections with horizontal rules: + +--- + +Like this one above. They create clear visual breaks in content. + +## Footnotes + +You can add footnotes[^1] to your content. They appear at the bottom of the page[^2]. + +[^1]: This is a footnote. It provides additional context without interrupting the flow. +[^2]: Footnotes are automatically numbered and linked. + +## Shortcodes + +### Alert Shortcode + +Use the alert shortcode for callouts: + +{{< alert type="note" >}} +This is a custom alert using the shortcode syntax. It's an alternative to GitHub-style alerts. +{{< /alert >}} + +{{< alert type="warning" >}} +Warning alerts grab attention for important notices. +{{< /alert >}} + +## Table of Contents + +This page demonstrates the automatic table of contents: + +- **Desktop:** TOC appears in left sidebar +- **Tablet:** TOC is hidden for more reading space +- **Mobile:** TOC is completely hidden + +The TOC automatically: +- Tracks your scroll position +- Highlights the current section +- Links to all headings (H2-H4) +- Stays visible while scrolling (sticky) + +## Dark & Light Mode + +The theme supports both dark and light modes: + +### Dark Mode (Default) +- True black backgrounds (#000000) +- OLED-friendly +- Purple accents (#c084fc) +- Reduced eye strain in low light + +### Light Mode +- Clean white backgrounds +- High contrast for daylight reading +- Purple accents (#a855f7) +- Print-friendly + +### System Mode +- Automatically matches OS preference +- Respects user's system settings +- Seamless switching + +Users can toggle between modes using the theme switcher in the navigation. + +## Search Functionality + +Press **Ctrl/Cmd + K** to try the search: + +1. Type your query +2. Results appear instantly +3. Use arrow keys to navigate +4. Press Enter to visit page +5. ESC to close + +The search: +- Is client-side (no server needed) +- Searches titles and summaries +- Highlights matching text +- Works offline + +## Navigation + +The theme includes: + +### Header Navigation +- Logo/brand name +- Main menu links +- Theme toggle + +### Footer +- Copyright notice +- Social links +- Attribution + +### Floating Dock (Bottom Right) +- Quick actions +- Scroll to top + +## Responsive Design + +The theme is fully responsive: + +### Mobile (< 640px) +- Single column layout +- Collapsible menu +- Touch-friendly buttons +- Optimized images + +### Tablet (641px - 1023px) +- Two column where appropriate +- Adjusted spacing +- Tablet-optimized navigation + +### Desktop (1024px+) +- Full layout with TOC sidebar +- Wider content area +- Hover effects +- Keyboard shortcuts + +## Performance + +The theme is optimized for performance: + +- **Minimal JavaScript** +- **Optimized CSS** +- **Fast Loading** +- **SEO Optimized** — Meta tags, structured data + +## Customization Examples + +### Changing Accent Color + +Edit `assets/css/base.css`: + +```css +:root { + --color-accent: #10b981; /* Green instead of purple */ +} +``` + +### Adding Custom Fonts + +In your `hugo.toml`: + +```toml +[params] + customFonts = ["https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap"] +``` + +### Custom CSS + +Create `assets/css/custom.css`: + +```css +.my-custom-element { + /* Your styles */ +} +``` + +## Best Practices + +### Content Organization + +1. Use clear, descriptive titles +2. Break content into sections with headings +3. Add tags and categories +4. Include descriptions in frontmatter +5. Use featured images for posts + +### Performance + +1. Optimize images before uploading +2. Use SVG for logos and icons +3. Minimize custom JavaScript +4. Leverage Hugo's asset pipeline + +### SEO + +1. Write descriptive meta descriptions +2. Use semantic HTML headings +3. Add alt text to images +4. Create an XML sitemap +5. Submit to search engines + +## Conclusion + +This guide covered all major features of the Minimal Black theme. You now know how to: + +- ✅ Format text and create rich content +- ✅ Use code blocks with syntax highlighting +- ✅ Add images and galleries +- ✅ Create diagrams with Mermaid +- ✅ Organize content with TOC +- ✅ Customize colors and styles +- ✅ Optimize for performance +- ✅ Make content SEO-friendly + +Start creating amazing content with Minimal Black! + +--- + +**Documentation:** [Full Docs](https://gitlab.com/jimchr12/hugo-minimal-black) + +**Repository:** [GitLab](https://gitlab.com/jimchr12/hugo-minimal-black) + +**Issues:** [Report a Bug](https://gitlab.com/jimchr12/hugo-minimal-black/issues) diff --git a/themes/minimal-black/exampleSite/content/projects/_index.md b/themes/minimal-black/exampleSite/content/projects/_index.md new file mode 100644 index 0000000..e69de29 diff --git a/themes/minimal-black/exampleSite/content/projects/sample-project.md b/themes/minimal-black/exampleSite/content/projects/sample-project.md new file mode 100644 index 0000000..7a6a04c --- /dev/null +++ b/themes/minimal-black/exampleSite/content/projects/sample-project.md @@ -0,0 +1,321 @@ ++++ +title = "Minimal Black Hugo Theme" +date = "2025-01-15" +description = "A minimal, dark-mode first Hugo theme" +github = "https://gitlab.com/jimchr12/hugo-minimal-black" +demo = "https://minimal-black-demo.netlify.app" +tags = ["hugo", "theme", "web-development", "open-source"] +categories = ["web"] +featured = true ++++ + +A comprehensive Hugo theme designed for developers, designers, and everyone in general who value simplicity, performance, and beautiful dare I say dark mode design (maybe?). + +## Overview + +Minimal Black is a modern Hugo theme that combines aesthetic appeal with practical functionality. + +Built with Tailwind CSS and a modular architecture, it provides everything you need for a professional personal website, portfolio or blog. + +### Key Features + +- **True Black Dark Mode** — With purple accents +- **Responsive Design** — Perfect on mobile, tablet, and desktop +- **Search Functionality** — Fast client-side search with Ctrl/Cmd+K +- **Table of Contents** — Auto-generated +- **Syntax Highlighting** — Beautiful code blocks with copy functionality +- **Multiple Layouts** — Flexible page templates for different content types (more to come hopefully). + +## Technical Architecture + +### Frontend Stack + +- **Hugo Extended** (v0.120.0+) — Static site generator +- **Tailwind CSS** — Utility-first CSS framework +- **Vanilla JS** — Minimal JS used - no framework +- **PostCSS** — CSS processing and optimization + +### CSS Modular Structure + +The CSS is organized into logical modules: + +``` +assets/css/ +├── base.css # Theme variables & Tailwind +├── utilities.css # Helper classes +├── components/ # Reusable UI components +│ ├── dock.css +│ ├── cards.css +│ ├── navigation.css +│ ├── search.css +│ └── tech-marquee.css +├── content/ # Content-specific styles +│ ├── markdown.css +│ └── toc.css +└── pages/ # Page-specific styles + ├── about.css + └── about-alternative.css +``` + +This modular approach makes the theme: +- Easy to maintain and extend +- Simple to customize specific components +- Clear separation of concerns +- Better developer experience + +## Design Philosophy + +### Minimalism First + +The theme embraces minimalism without sacrificing functionality: + +1. **Clean Typography** — Careful font choices and spacing +2. **Focused Content** — Remove distractions, highlight what matters +3. **Subtle Animations** — Smooth transitions that enhance UX +4. **Dark Mode Priority** — True black backgrounds + +## Feature Showcase + +### Search Functionality + +Built-in search with keyboard shortcuts: + +- Press **Ctrl/Cmd + K** to open search +- Type to filter results in real-time +- Use **Arrow keys** to navigate +- Press **Enter** to visit page +- **ESC** to close + +The search is client-side, fast, and requires no external services. + +### Code Blocks + +Beautiful syntax highlighting with practical features: + +```javascript +// Copy button on hover +function greet(name) { + console.log(`Hello, ${name}!`); + return `Welcome to Minimal Black Theme`; +} + +greet("Developer"); +``` + +Features include: + +- Language labels +- Copy to clipboard button +- Line highlighting +- Collapsible / Expandable block + +### GitHub-Style Alerts + +Support for callout boxes: + +> [!NOTE] +> This is a note alert for informational content. + +> [!TIP] +> This is a tip alert with helpful suggestions. + +> [!WARNING] +> This is a warning alert for important notices. + +> [!IMPORTANT] +> This is an important alert for critical information. + +### Table of Contents + +MD content automatically gets a TOC: + +- **Sticky Positioning** — Stays visible while scrolling +- **Active Tracking** — Highlights current section +- **Smooth Scrolling** — Click to jump to section +- **Responsive** — Hides on mobile, shows on desktop + +You're seeing it in action on this **page**! + +### Mermaid Diagrams + +Native support for flowcharts and diagrams: + +```mermaid +graph TD + A[User visits site] --> B{Dark mode?} + B -->|Yes| C[Load dark theme] + B -->|No| D[Load light theme] + C --> E[Render content] + D --> E + E --> F[Fast, beautiful site] +``` + +Perfect for technical documentation. + +## Configuration + +The theme is highly configurable via `hugo.toml`: + +### Basic Setup + +```toml +[params] + brand = "Your Name" + description = "Your site description" + + [params.theme] + defaultTheme = "dark" # or "light" or "system" +``` + +### Hero Section + +```toml +[params.hero] + badge = "Software Engineer" + title = "Hi, I'm Your Name." + role = "Building things." + summary = "Description of what you do." + location = "City, Country" +``` + +### Tech Stack Display + +```toml +[[params.home.tech]] + label = "Python" + icon = "devicon-python-plain" + +[[params.home.tech]] + label = "Docker" + icon = "devicon-docker-plain" +``` + +### Analytics + +Support for multiple providers: + +```toml +[params.analytics] + googleAnalytics = "G-XXXXXXXXXX" + + # Or use Plausible, Umami, Fathom +``` + +## Customization Guide + +### Colors + +Theme colors are CSS custom properties in `assets/css/base.css`: + +```css +:root { + --color-bg: #f9fafb; /* Light background */ + --color-accent: #a855f7; /* Purple accent */ +} + +html[data-theme="dark"] { + --color-bg: #000000; /* True black */ + --color-accent: #c084fc; /* Lighter purple */ +} +``` + +### Adding Custom Styles + +Create `assets/css/custom.css` in your site: + +```css +/* Your custom styles */ +.my-custom-class { + /* ... */ +} +``` + +Then reference it in your config: + +```toml +[params] + customCSS = ["css/custom.css"] +``` + +## Content Organization + +### Recommended Structure + +``` +content/ +├── _index.md # Homepage +├── about.md # About page +├── blog/ +│ ├── _index.md +│ └── posts/ +├── projects/ +│ ├── _index.md +│ └── individual-projects/ +└── pages/ + └── custom-pages/ +``` + +### Frontmatter Examples + +**Blog Post:** +```yaml ++++ +title = "Post Title" +date = "2025-01-15" +tags = ["hugo", "web-dev"] +categories = ["tutorials"] +draft = false ++++ +``` + +**Project:** +```yaml ++++ +title = "Project Name" +description = "Brief description" +github = "https://github.com/..." # Optional +demo = "https://demo.com" # Optional +featured = true ++++ +``` + +## Deployment + +### Netlify + +1. Connect your Git repository +2. Build command: `hugo --minify` +3. Publish directory: `public` +4. Environment: `HUGO_VERSION=0.120.0` + +### Vercel + +1. Import repository +2. Framework: Hugo +3. Build command: `cd themes/minimal-black && npm install && cd ../.. && hugo --minify` +4. Output: `public` + +## Contributing + +Contributions are welcome! See [CONTRIBUTING.md](https://gitlab.com/jimchr12/hugo-minimal-black/blob/main/CONTRIBUTING.md) for guidelines. + +## License + +Released under the MIT License. See [LICENSE](https://gitlab.com/jimchr12/hugo-minimal-black/blob/main/LICENSE) for details. + +## Acknowledgments + +Built with: + +- [Hugo](https://gohugo.io/) — Static site generator +- [Tailwind CSS](https://tailwindcss.com/) — CSS framework +- [Font Awesome](https://fontawesome.com/) — Icons +- [Devicon](https://devicon.dev/) — Technology icons + +--- + +**Repository:** [GitLab](https://gitlab.com/jimchr12/hugo-minimal-black) + +**Demo:** [Live Preview](https://minimal-black-demo.netlify.app) + +**Documentation:** [Full Docs](https://gitlab.com/jimchr12/hugo-minimal-black#readme) diff --git a/themes/minimal-black/exampleSite/hugo.toml b/themes/minimal-black/exampleSite/hugo.toml new file mode 100644 index 0000000..5ced6ee --- /dev/null +++ b/themes/minimal-black/exampleSite/hugo.toml @@ -0,0 +1,362 @@ +baseURL = 'https://minimal-black-demo.netlify.app' +languageCode = 'en-us' +title = 'Minimal Black Theme' +theme = "minimal-black" + +# Enable search index generation and web app manifest +[outputs] + home = ["HTML", "RSS", "JSON", "WebAppManifest"] + +[outputFormats.WebAppManifest] + mediaType = "application/manifest+json" + rel = "manifest" + baseName = "manifest" + isPlainText = true + notAlternative = true + +[params] + brand = "Hugo Minimal Black" + description = "A minimal, dark-mode first personal site, or whatever else you want it to be about" + + # Favicon - Place your favicon files in /static/ directory + # Supported formats: .ico, .png, .svg + favicon = "icons/favicon.svg" + appleTouchIcon = "apple-touch-icon.png" + + # Logo (optional) + logo = "images/logo.png" + + # Project and blog intro text + projectsIntro = "Selected projects to display list." # Can be empty + blogIntro = "" # Can be empty + + # Web App Manifest Configuration (PWA support) + [params.manifest] + themeColor = "#a855f7" + backgroundColor = "#000000" + categories = ["blog", "portfolio", "developer"] + + # Custom icon configuration (optional) + # If not specified, defaults to /icons/android-chrome-*.png + # [[params.manifest.icons]] + # src = "/icons/android-chrome-192x192.png" + # sizes = "192x192" + # type = "image/png" + # purpose = "any maskable" + + # Theme Configuration + [params.theme] + defaultTheme = "dark" # Options: "light", "dark", "system" + + # Home Page Configuration + [params.home] + sections = ["hero", "now", "tech-marquee", "projects", "posts"] + showNowSection = true + showFeaturedProjects = true + showLatestPosts = true + featuredProjectsLimit = 3 + latestPostsLimit = 3 + projectsTitle = "Selected Work" + projectsSubtitle = "" + blogTitle = "Latest Writing" + blogSubtitle = "" + techMarqueeLabel = "Experienced In" + + # Technology Stack Display + [[params.home.tech]] + label = "Python" + icon = "devicon-python-plain" + + [[params.home.tech]] + label = "Java" + icon = "devicon-java-plain" + + [[params.home.tech]] + label = "Spring" + icon = "devicon-spring-plain" + + [[params.home.tech]] + label = "Vaadin" + icon = "devicon-vaadin-plain" + + [[params.home.tech]] + label = "Debian" + icon = "devicon-debian-plain" + + [[params.home.tech]] + label = "GitLab" + icon = "devicon-gitlab-plain" + + [[params.home.tech]] + label = "Docker" + icon = "devicon-docker-plain" + + [[params.home.techReverse]] + label = "PostgreSQL" + icon = "devicon-postgresql-plain" + + [[params.home.techReverse]] + label = "MongoDB" + icon = "devicon-mongodb-plain" + + [[params.home.techReverse]] + label = "Git" + icon = "devicon-git-plain" + + [[params.home.techReverse]] + label = "Hugo" + icon = "devicon-hugo-plain" + + [[params.home.techReverse]] + label = "Jenkins" + icon = "devicon-jenkins-plain" + + [[params.home.techReverse]] + label = "JetBrains" + icon = "devicon-jetbrains-plain" + + [[params.home.techReverse]] + label = "Json" + icon = "devicon-json-plain" + + [[params.home.techReverse]] + label = "Linux" + icon = "devicon-linux-plain" + + [[params.home.techReverse]] + label = "Liquibase" + icon = "devicon-liquibase-plain" + + [[params.home.techReverse]] + label = "Markdown" + icon = "devicon-markdown-original" + + [[params.home.techReverse]] + label = "YAML" + icon = "devicon-yaml-plain" + + [[params.home.techReverse]] + label = "Wordpress" + icon = "devicon-wordpress-plain" + + [[params.home.techReverse]] + label = "WooCommerce" + icon = "devicon-woocommerce-plain" + + [[params.home.techReverse]] + label = "Traefik" + icon = "devicon-traefikproxy-plain" + + [[params.home.techReverse]] + label = "pfSense" + icon = "devicon-pfsense-plain" + + # About Page Alternative Layout Configuration - Optional + [params.about.alt] + # Stats displayed in the profile card sidebar + [[params.about.alt.stats]] + value = "5+" + label = "Years Coding" + + [[params.about.alt.stats]] + value = "20+" + label = "Projects" + + [[params.about.alt.stats]] + value = "∞" + label = "Hours Spent" + + # Skills/Tech Stack badges with icons + [[params.about.alt.skills]] + label = "JavaScript" + icon = "devicon-javascript-plain" + + [[params.about.alt.skills]] + label = "Python" + icon = "devicon-python-plain" + + [[params.about.alt.skills]] + label = "React" + icon = "devicon-react-original" + + [[params.about.alt.skills]] + label = "Docker" + icon = "devicon-docker-plain" + + [[params.about.alt.skills]] + label = "PostgreSQL" + icon = "devicon-postgresql-plain" + + [[params.about.alt.skills]] + label = "AWS" + icon = "devicon-amazonwebservices-plain" + + # Hero Section Configuration + [params.hero] + badge = "Software Engineer" + title = "Hi, I'm Your Name or your interesting title." + role = "Subtitle for title with role perspective" + summary = "You can write your summary to be displayed here." + # avatar = "images/avatar.jpg" # Optional: 400x400px recommended + location = "City, Country" + focus = "Currently focused on Hugo themes & developer experience." + available = true + availableLabel = "Available for work" + + nowLabel = "Quick Facts" + nowIntro = "Right now I'm mainly:" + now = [ + "Building minimal Hugo themes", + "Exploring dark-mode design patterns", + "Writing about web performance" + ] + + [params.hero.primary] + label = "View Projects" + href = "/projects/" + + [params.hero.secondary] + label = "Read the Blog" + href = "/blog/" + + # Icon Libraries + [params.icons] + useFontAwesome = true + useDevicon = true + + # Social Links + [[params.social]] + label = "GitLab" + url = "https://gitlab.com/jimchr12" + icon = "fa-brands fa-gitlab" + + [[params.social]] + label = "LinkedIn" + url = "https://www.linkedin.com/in/jimchristopoulos-542512221/" + icon = "fa-brands fa-linkedin-in" + + [[params.social]] + label = "Email" + url = "mailto:you@example.com" + icon = "fa-regular fa-envelope" + + # Analytics Configuration + [params.analytics] + # Google Analytics (GA4) + # googleAnalytics = "G-XXXXXXXXXX" + + # Plausible Analytics (privacy-friendly) + # [params.analytics.plausible] + # enabled = true + # domain = "yourdomain.com" + # scriptUrl = "https://plausible.io/js/script.js" + + # Umami Analytics (self-hosted option) + # [params.analytics.umami] + # enabled = true + # scriptUrl = "https://analytics.yourdomain.com/script.js" + # websiteId = "your-website-id" + + # Fathom Analytics + # [params.analytics.fathom] + # enabled = true + # scriptUrl = "https://cdn.usefathom.com/script.js" + # siteId = "YOUR-SITE-ID" + +# Navigation Menu +[menu] + [[menu.main]] + name = "Home" + pageRef = "/" + url = "/" + weight = 1 + identifier = "home" + [menu.main.params] + icon = "fa-solid fa-house" + + [[menu.main]] + name = "About" + pageRef = "about" + url = "/about/" + weight = 2 + identifier = "about" + [menu.main.params] + icon = "fa-regular fa-user" + + [[menu.main]] + name = "About Alt" + pageRef = "about-alternative" + url = "/about-alternative/" + weight = 2 + identifier = "about-alternative" + [menu.main.params] + icon = "fa-solid fa-user" + + [[menu.main]] + name = "Projects" + pageRef = "projects" + url = "/projects/" + weight = 3 + identifier = "projects" + [menu.main.params] + icon = "fa-regular fa-folder-open" + + [[menu.main]] + name = "Blog" + pageRef = "blog" + url = "/blog/" + weight = 4 + identifier = "blog" + [menu.main.params] + icon = "fa-regular fa-note-sticky" + +# Markup Configuration +[markup] + # Table of Contents + [markup.tableOfContents] + startLevel = 2 + endLevel = 4 + + # Goldmark Renderer (Markdown) + [markup.goldmark.renderer] + unsafe = true + + [markup.goldmark.parser] + [markup.goldmark.parser.attribute] + block = true + + [markup.goldmark.extensions] + typographer = true + linkify = true + table = true + strikethrough = true + taskList = true + definitionList = true + footnote = true + + [markup.goldmark.extensions.extras.delete] + enable = true + [markup.goldmark.extensions.extras.insert] + enable = true + [markup.goldmark.extensions.extras.mark] + enable = true + + # Syntax Highlighting + [markup.highlight] + codeFences = true + guessSyntax = true + lineNos = false + lineNumbersInTable = false + noClasses = false + style = "monokai" + tabWidth = 2 + +# Taxonomies +[taxonomies] + tag = "tags" + category = "categories" + +# Privacy Configuration +[privacy] + [privacy.youtube] + privacyEnhanced = true diff --git a/themes/minimal-black/images/about-alt.png b/themes/minimal-black/images/about-alt.png new file mode 100644 index 0000000..09a2462 Binary files /dev/null and b/themes/minimal-black/images/about-alt.png differ diff --git a/themes/minimal-black/images/about.png b/themes/minimal-black/images/about.png new file mode 100644 index 0000000..3c074b1 Binary files /dev/null and b/themes/minimal-black/images/about.png differ diff --git a/themes/minimal-black/images/screenshot.png b/themes/minimal-black/images/screenshot.png new file mode 100644 index 0000000..01f70cf Binary files /dev/null and b/themes/minimal-black/images/screenshot.png differ diff --git a/themes/minimal-black/images/tn.png b/themes/minimal-black/images/tn.png new file mode 100644 index 0000000..d3c5092 Binary files /dev/null and b/themes/minimal-black/images/tn.png differ diff --git a/themes/minimal-black/layouts/404.html b/themes/minimal-black/layouts/404.html new file mode 100644 index 0000000..b5433cc --- /dev/null +++ b/themes/minimal-black/layouts/404.html @@ -0,0 +1,308 @@ +{{ define "main" }} +
+
+
+ +
404
+ + +

Page Not Found

+

+ The page you're looking for doesn't exist or has been moved. +

+ + + + + + + + + {{ $recentPages := where .Site.RegularPages "Section" "in" (slice "blog" "projects") }} + {{ $recentPages = first 3 $recentPages }} + {{ if $recentPages }} + + {{ end }} +
+
+
+ + +{{ end }} diff --git a/themes/minimal-black/layouts/_default/_markup/render-blockquote-mermaid.html b/themes/minimal-black/layouts/_default/_markup/render-blockquote-mermaid.html new file mode 100644 index 0000000..9016162 --- /dev/null +++ b/themes/minimal-black/layouts/_default/_markup/render-blockquote-mermaid.html @@ -0,0 +1,7 @@ +
+
+
+{{ .Text | safeHTML }}
+    
+
+
diff --git a/themes/minimal-black/layouts/_default/_markup/render-blockquote.html b/themes/minimal-black/layouts/_default/_markup/render-blockquote.html new file mode 100644 index 0000000..0a43e6c --- /dev/null +++ b/themes/minimal-black/layouts/_default/_markup/render-blockquote.html @@ -0,0 +1,10 @@ +{{- /* Check if this is a GitHub-style alert */ -}} +{{- if eq .Type "alert" -}} +
+ {{ .Text | safeHTML }} +
+{{- else -}} +
+ {{ .Text | safeHTML }} +
+{{- end -}} diff --git a/themes/minimal-black/layouts/_default/_markup/render-codeblock-mermaid.html b/themes/minimal-black/layouts/_default/_markup/render-codeblock-mermaid.html new file mode 100644 index 0000000..0b638e9 --- /dev/null +++ b/themes/minimal-black/layouts/_default/_markup/render-codeblock-mermaid.html @@ -0,0 +1,5 @@ +
+
+{{ .Inner | safeHTML }}
+  
+
diff --git a/themes/minimal-black/layouts/_default/_markup/render-codeblock.html b/themes/minimal-black/layouts/_default/_markup/render-codeblock.html new file mode 100644 index 0000000..17f4e66 --- /dev/null +++ b/themes/minimal-black/layouts/_default/_markup/render-codeblock.html @@ -0,0 +1,337 @@ +{{- $lang := .Type | default "text" -}} +{{- $filename := .Attributes.filename | default "" -}} +{{- $label := cond (ne $filename "") $filename ($lang | upper) -}} +{{- $id := printf "cb-%s" (printf "%d" .Ordinal | sha256 | truncate 8 "") -}} + +{{- $collapseEnabled := site.Params.codeblock.collapse.enabled | default true -}} +{{- $defaultState := site.Params.codeblock.collapse.defaultState | default "expanded" -}} +{{- $collapsedHeight := site.Params.codeblock.collapse.collapsedHeight | default 200 -}} + +{{- $highlighted := transform.HighlightCodeBlock . -}} + +
+ +
+
+ + {{ $lang | upper }} + + {{- if ne $filename "" -}} + + + {{ $filename }} + + {{- end -}} +
+ +
+ {{- if $collapseEnabled -}} + + {{- end -}} + +
+
+ + +
+ {{ $highlighted.Wrapped }} + + {{- if $collapseEnabled -}} +
+ +
+ {{- end -}} +
+
+ + + + \ No newline at end of file diff --git a/themes/minimal-black/layouts/_default/_markup/render-heading.html b/themes/minimal-black/layouts/_default/_markup/render-heading.html new file mode 100644 index 0000000..f522faf --- /dev/null +++ b/themes/minimal-black/layouts/_default/_markup/render-heading.html @@ -0,0 +1,5 @@ + + + {{ .Text | safeHTML }} + + diff --git a/themes/minimal-black/layouts/_default/_markup/render-image.html b/themes/minimal-black/layouts/_default/_markup/render-image.html new file mode 100644 index 0000000..521c3c6 --- /dev/null +++ b/themes/minimal-black/layouts/_default/_markup/render-image.html @@ -0,0 +1,12 @@ +
+ + {{ .Text }} + + {{ with .Title }} +
{{ . }}
+ {{ end }} +
diff --git a/themes/minimal-black/layouts/_default/_markup/render-link.html b/themes/minimal-black/layouts/_default/_markup/render-link.html new file mode 100644 index 0000000..c5c0368 --- /dev/null +++ b/themes/minimal-black/layouts/_default/_markup/render-link.html @@ -0,0 +1,7 @@ + + {{ .Text | safeHTML }} + diff --git a/themes/minimal-black/layouts/_default/_markup/render-table.html b/themes/minimal-black/layouts/_default/_markup/render-table.html new file mode 100644 index 0000000..173f355 --- /dev/null +++ b/themes/minimal-black/layouts/_default/_markup/render-table.html @@ -0,0 +1,39 @@ +
+ + + {{- range .THead }} + + {{- range . }} + + {{- end }} + + {{- end }} + + + {{- range .TBody }} + + {{- range . }} + + {{- end }} + + {{- end }} + +
+ {{- .Text -}} +
+ {{- .Text -}} +
+
diff --git a/themes/minimal-black/layouts/_default/_shortcodes/alert.html b/themes/minimal-black/layouts/_default/_shortcodes/alert.html new file mode 100644 index 0000000..10c8789 --- /dev/null +++ b/themes/minimal-black/layouts/_default/_shortcodes/alert.html @@ -0,0 +1,4 @@ +{{- $type := .Get "type" | default "note" -}} +
+ {{ .Inner | markdownify }} +
diff --git a/themes/minimal-black/layouts/_default/_shortcodes/gallery.html b/themes/minimal-black/layouts/_default/_shortcodes/gallery.html new file mode 100644 index 0000000..e9ec640 --- /dev/null +++ b/themes/minimal-black/layouts/_default/_shortcodes/gallery.html @@ -0,0 +1,35 @@ +{{- $images := split (.Get "images") "," -}} +{{- $captions := split (default "" (.Get "captions")) "," -}} + + diff --git a/themes/minimal-black/layouts/_default/about-alternative.html b/themes/minimal-black/layouts/_default/about-alternative.html new file mode 100644 index 0000000..20f492a --- /dev/null +++ b/themes/minimal-black/layouts/_default/about-alternative.html @@ -0,0 +1,156 @@ +{{ define "main" }} +
+
+ +
+ + + + +
+ +
+
+ {{ $content := .Content }} + {{ $content = replace $content "
" "|||SPLIT|||" }} + {{ $content = replace $content "
" "|||SPLIT|||" }} + {{ $content = replace $content "
" "|||SPLIT|||" }} + {{ $parts := split $content "|||SPLIT|||" }} + + {{ index $parts 0 | safeHTML }} +
+
+ + + {{ if gt (len $parts) 1 }} + {{- $experienceParts := slice -}} + {{- $additionalContent := "" -}} + {{- $foundNonExperience := false -}} + + {{- range $index, $part := after 1 $parts -}} + {{- $trimmed := trim $part " \n\t" -}} + {{- if $trimmed -}} + {{- /* Check if this looks like an experience card (starts with

) or is additional content (starts with ") (hasPrefix $trimmed "

\n +

+ + Experience +

+ +
+ {{- range $experienceParts -}} +
+ {{ . | safeHTML }} +
+ {{- end -}} +
+
+ {{- end -}} + + {{- /* Render additional content sections */ -}} + {{- if $additionalContent -}} + {{- $additionalParts := split $additionalContent "|||SPLIT|||" -}} + {{- range $additionalParts -}} + {{- $trimmed := trim . " \n\t" -}} + {{- if $trimmed -}} +
+
+ {{ . | safeHTML }} +
+
+ {{- end -}} + {{- end -}} + {{- end -}} + {{ end }} + + + {{ with $.Site.Params.about.alt.skills }} +
+

+ + Tech Stack +

+
+ {{ range . }} + + {{ with .icon }}{{ end }} + {{ .label }} + + {{ end }} +
+
+ {{ end }} +
+ + +
+
+{{ end }} diff --git a/themes/minimal-black/layouts/_default/about.html b/themes/minimal-black/layouts/_default/about.html new file mode 100644 index 0000000..468010f --- /dev/null +++ b/themes/minimal-black/layouts/_default/about.html @@ -0,0 +1,77 @@ +{{ define "main" }} +
+
+ +
+
+ {{ with .Site.Params.hero.avatar }} +
+ {{ $.Site.Params.brand }} +
+ {{ end }} + +

{{ .Title }}

+ + {{ with .Params.subtitle }} +

{{ . }}

+ {{ end }} +
+
+ + +
+
+ {{ $content := .Content }} + {{ $content = replace $content "
" "|||SPLIT|||" }} + {{ $content = replace $content "
" "|||SPLIT|||" }} + {{ $content = replace $content "
" "|||SPLIT|||" }} + {{ $parts := split $content "|||SPLIT|||" }} + + {{ if eq (len $parts) 1 }} + + {{ .Content }} + {{ else }} + + {{ index $parts 0 | safeHTML }} + + +
+ {{ range after 1 $parts }} + {{ $trimmed := trim . " \n\t" }} + {{ if $trimmed }} +
+
+
+ {{ . | safeHTML }} +
+
+ {{ end }} + {{ end }} +
+ {{ end }} +
+
+ + + {{ with .Site.Params.social }} +
+

Let's Connect

+ +
+ {{ end }} +
+
+{{ end }} diff --git a/themes/minimal-black/layouts/_default/baseof.html b/themes/minimal-black/layouts/_default/baseof.html new file mode 100644 index 0000000..39ebf71 --- /dev/null +++ b/themes/minimal-black/layouts/_default/baseof.html @@ -0,0 +1,30 @@ + + + + {{ partial "head.html" . }} + + +
+ {{ partial "header.html" . }} + +
+ {{ block "main" . }}{{ end }} +
+ + {{ partial "footer.html" . }} +
+ {{ partial "search-overlay.html" . }} + {{ partial "dock.html" . }} + + + + + + + + diff --git a/themes/minimal-black/layouts/_default/list.html b/themes/minimal-black/layouts/_default/list.html new file mode 100644 index 0000000..0ddef5f --- /dev/null +++ b/themes/minimal-black/layouts/_default/list.html @@ -0,0 +1,30 @@ +{{ define "main" }} +
+
+
+

{{ .Title }}

+ {{ with .Description }} +

{{ . }}

+ {{ end }} +
+ + +
+ {{ end }} + + \ No newline at end of file diff --git a/themes/minimal-black/layouts/_default/single.html b/themes/minimal-black/layouts/_default/single.html new file mode 100644 index 0000000..7ca425d --- /dev/null +++ b/themes/minimal-black/layouts/_default/single.html @@ -0,0 +1,154 @@ +{{ define "main" }} +
+
+
+
+

{{ .Title }}

+ +
+ {{ with .Date }} + {{ .Format "02 Jan 2006" }} + {{ end }} + {{ with .ReadingTime }} + • {{ . }} min read + {{ end }} +
+ + {{ with .Params.description }} +

+ {{ . }} +

+ {{ end }} +
+ +
+
+ {{ .Content }} +
+
+
+ + {{ if .TableOfContents }} + + + + {{ end }} +
+
+{{ end }} diff --git a/themes/minimal-black/layouts/blog/list.html b/themes/minimal-black/layouts/blog/list.html new file mode 100644 index 0000000..35ae38e --- /dev/null +++ b/themes/minimal-black/layouts/blog/list.html @@ -0,0 +1,48 @@ +{{ define "main" }} +
+
+
+

Blog

+ {{ with .Site.Params.blogIntro }} +

+ {{ . }} +

+ {{ else }} +

+ Notes on engineering, design, and building small, thoughtful tools. +

+ {{ end }} +
+ + {{/* Paginate all posts in this section */}} + {{ $paginator := .Paginate .Pages }} + +
+ {{ range $paginator.Pages.ByDate.Reverse }} + {{ partial "components/post-card.html" (dict "Page" . "Root" $) }} + {{ end }} +
+ + {{/* Simple pagination controls */}} + {{ if gt $paginator.TotalPages 1 }} + + {{ end }} +
+
+{{ end }} diff --git a/themes/minimal-black/layouts/index.html b/themes/minimal-black/layouts/index.html new file mode 100644 index 0000000..459e3aa --- /dev/null +++ b/themes/minimal-black/layouts/index.html @@ -0,0 +1,12 @@ +{{ define "main" }} +
+
+ {{ $default := slice "hero" "now" "tech-marquee" "projects" "posts" }} + {{ $sections := .Site.Params.home.sections | default $default }} + + {{ range $sections }} + {{ partial (printf "home/%s.html" .) $ }} + {{ end }} +
+
+{{ end }} diff --git a/themes/minimal-black/layouts/index.json b/themes/minimal-black/layouts/index.json new file mode 100644 index 0000000..f821f1a --- /dev/null +++ b/themes/minimal-black/layouts/index.json @@ -0,0 +1,17 @@ +{{- $pages := slice -}} + +{{- range .Site.RegularPages -}} + {{- $summary := .Summary -}} + {{- if not $summary -}} + {{- $summary = .Description -}} + {{- end -}} + + {{- $pages = $pages | append (dict + "title" .Title + "permalink" .Permalink + "section" .Section + "summary" (plainify $summary) + ) -}} +{{- end -}} + +{{- dict "pages" $pages | jsonify -}} diff --git a/themes/minimal-black/layouts/index.webappmanifest b/themes/minimal-black/layouts/index.webappmanifest new file mode 100644 index 0000000..8a48504 --- /dev/null +++ b/themes/minimal-black/layouts/index.webappmanifest @@ -0,0 +1,61 @@ +{{- $title := .Site.Title -}} +{{- $shortName := .Site.Params.brand | default .Site.Title -}} +{{- $description := .Site.Params.description | default "A minimal, dark-mode first personal site" -}} +{{- $themeColor := .Site.Params.manifest.themeColor | default "#a855f7" -}} +{{- $bgColor := .Site.Params.manifest.backgroundColor | default "#000000" -}} +{{- $lang := .Site.Language.Lang | default "en" -}} +{ + "name": "{{ $title }}", + "short_name": "{{ $shortName }}", + "description": "{{ $description }}", + "start_url": "{{ "/" | relURL }}", + "scope": "{{ "/" | relURL }}", + "display": "standalone", + "background_color": "{{ $bgColor }}", + "theme_color": "{{ $themeColor }}", + "orientation": "portrait-primary", + "icons": [ + {{- if .Site.Params.manifest.icons }} + {{- range $i, $icon := .Site.Params.manifest.icons }} + {{- if $i }},{{ end }} + { + "src": "{{ $icon.src | relURL }}", + "sizes": "{{ $icon.sizes }}", + "type": "{{ $icon.type | default "image/png" }}" + {{- with $icon.purpose }},"purpose": "{{ . }}"{{ end }} + } + {{- end }} + {{- else }} + { + "src": "/icons/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "any maskable" + }, + { + "src": "/icons/android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "any maskable" + }, + { + "src": "/icons/apple-touch-icon.png", + "sizes": "180x180", + "type": "image/png" + }, + { + "src": "/icons/favicon-32x32.png", + "sizes": "32x32", + "type": "image/png" + }, + { + "src": "/icons/favicon-16x16.png", + "sizes": "16x16", + "type": "image/png" + } + {{- end }} + ], + "categories": "{{ .Site.Params.manifest.categories | default (slice "blog" "portfolio" "developer") }}", + "lang": "{{ $lang }}", + "dir": "ltr" +} diff --git a/themes/minimal-black/layouts/partials/analytics.html b/themes/minimal-black/layouts/partials/analytics.html new file mode 100644 index 0000000..a2c5535 --- /dev/null +++ b/themes/minimal-black/layouts/partials/analytics.html @@ -0,0 +1,46 @@ +{{- if not hugo.IsServer -}} + + +{{ with .Site.Params.analytics.googleAnalytics }} + {{ if . }} + + + {{ end }} +{{ end }} + + +{{ with .Site.Params.analytics.plausible }} + {{ if .enabled }} + + {{ end }} +{{ end }} + + +{{ with .Site.Params.analytics.umami }} + {{ if .enabled }} + + {{ end }} +{{ end }} + + +{{ with .Site.Params.analytics.fathom }} + {{ if .enabled }} + + {{ end }} +{{ end }} + + +{{ with .Site.Params.analytics.custom }} + {{ if .head }} + {{ range .head }} + {{ . | safeHTML }} + {{ end }} + {{ end }} +{{ end }} + +{{- end -}} diff --git a/themes/minimal-black/layouts/partials/components/post-card.html b/themes/minimal-black/layouts/partials/components/post-card.html new file mode 100644 index 0000000..587558e --- /dev/null +++ b/themes/minimal-black/layouts/partials/components/post-card.html @@ -0,0 +1,53 @@ +{{/* props: Page (post page) */}} +{{- $p := .Page -}} +{{- $icon := $p.Params.icon | default "fa-regular fa-file-lines" -}} +{{- $category := $p.Params.category | default "Article" -}} + + diff --git a/themes/minimal-black/layouts/partials/components/project-card.html b/themes/minimal-black/layouts/partials/components/project-card.html new file mode 100644 index 0000000..ba40a4d --- /dev/null +++ b/themes/minimal-black/layouts/partials/components/project-card.html @@ -0,0 +1,87 @@ +{{/* props: Page (project page) */}} +{{- $p := .Page -}} + +{{- $icon := $p.Params.icon | default "fa-solid fa-folder-tree" -}} +{{- $badge := cond ($p.Params.featured) "Featured" "" -}} + +{{- $repo := $p.Params.repo -}} +{{- $repoIcon := $p.Params.repoIcon | default "fa-brands fa-github" -}} +{{- $repoLabel := $p.Params.repoLabel | default "Repo" -}} + +{{- $demo := $p.Params.demo -}} +{{- $demoIcon := $p.Params.demoIcon | default "fa-solid fa-play" -}} +{{- $demoLabel := $p.Params.demoLabel | default "Demo" -}} + +{{- $website := $p.Params.website -}} +{{- $websiteIcon := $p.Params.websiteIcon | default "fa-solid fa-globe" -}} +{{- $websiteLabel := $p.Params.websiteLabel | default "Website" -}} + + diff --git a/themes/minimal-black/layouts/partials/components/section-header.html b/themes/minimal-black/layouts/partials/components/section-header.html new file mode 100644 index 0000000..18dac2e --- /dev/null +++ b/themes/minimal-black/layouts/partials/components/section-header.html @@ -0,0 +1,12 @@ +{{/* props: Title, Url, Small (bool) */}} {{- $title := .Title | default +"Section" -}} {{- $url := .Url -}} {{- $small := .Small | default false -}} + +
+

{{ $title }}

+ + {{ with $url }} + + {{ if $small }}All{{ else }}View all{{ end }} + + {{ end }} +
diff --git a/themes/minimal-black/layouts/partials/dark-toggle.html b/themes/minimal-black/layouts/partials/dark-toggle.html new file mode 100644 index 0000000..25cb113 --- /dev/null +++ b/themes/minimal-black/layouts/partials/dark-toggle.html @@ -0,0 +1,41 @@ + diff --git a/themes/minimal-black/layouts/partials/dock.html b/themes/minimal-black/layouts/partials/dock.html new file mode 100644 index 0000000..98abf40 --- /dev/null +++ b/themes/minimal-black/layouts/partials/dock.html @@ -0,0 +1,55 @@ +{{- $isHome := .IsHome -}} + +
+
+ +
+ {{ if not $isHome }} + + + + {{ end }} + + + + + + + + + +
+ + + +
+
diff --git a/themes/minimal-black/layouts/partials/footer.html b/themes/minimal-black/layouts/partials/footer.html new file mode 100644 index 0000000..777202f --- /dev/null +++ b/themes/minimal-black/layouts/partials/footer.html @@ -0,0 +1,56 @@ +
+
+ +
+
diff --git a/themes/minimal-black/layouts/partials/head.html b/themes/minimal-black/layouts/partials/head.html new file mode 100644 index 0000000..d19a90c --- /dev/null +++ b/themes/minimal-black/layouts/partials/head.html @@ -0,0 +1,103 @@ +{{- $title := cond (ne .Title "") (printf "%s | %s" .Title .Site.Title) +.Site.Title -}} + + + +{{ $title }} + +{{ partial "meta.html" . }} + + +{{ with .Site.Params.favicon }} + +{{ else }} + + {{ if fileExists "static/favicon.ico" }} + + {{ end }} + {{ if fileExists "static/favicon.png" }} + + {{ end }} + {{ if fileExists "static/favicon.svg" }} + + {{ end }} +{{ end }} + + +{{ if .Site.Params.appleTouchIcon }} + +{{ else if fileExists "static/apple-touch-icon.png" }} + +{{ end }} + + +{{ with .Site.GetPage "/" }} + {{ range .OutputFormats }} + {{ if eq .Name "webappmanifest" }} + + {{ end }} + {{ end }} +{{ end }} + + + +{{ if .Site.Params.icons.useFontAwesome }} + +{{ end }} + +{{ if .Site.Params.icons.useDevicon }} + +{{ end }} + + + + + + + + + + + + + +{{ partial "analytics.html" . }} diff --git a/themes/minimal-black/layouts/partials/header.html b/themes/minimal-black/layouts/partials/header.html new file mode 100644 index 0000000..aa368f6 --- /dev/null +++ b/themes/minimal-black/layouts/partials/header.html @@ -0,0 +1,86 @@ +
+ {{/* Brand string for logo/monogram */}} + {{- $brand := .Site.Params.brand | default .Site.Title}} + {{- $mono := upper (substr $brand 0 2) -}} + +
+ + +
+ + +
+ +
+
diff --git a/themes/minimal-black/layouts/partials/home/hero.html b/themes/minimal-black/layouts/partials/home/hero.html new file mode 100644 index 0000000..155360f --- /dev/null +++ b/themes/minimal-black/layouts/partials/home/hero.html @@ -0,0 +1,94 @@ +{{- $hero := .Site.Params.hero -}} + +
+
+ +
+ {{ with $hero.badge }} +

{{ . }}

+ {{ end }} + + {{ if $hero.available }} + + + {{ default "Available for work" $hero.availableLabel }} + + {{ end }} +
+ +
+

+ {{ default "Hi, I’m Your Name." $hero.title }} +

+ {{ with $hero.role }} +

+ {{ . }} +

+ {{ end }} +
+ + {{ with $hero.summary }} +

+ {{ . }} +

+ {{ end }} + + +
+ {{ with $hero.location }} + + + {{ . }} + + {{ end }} + + {{ with $hero.focus }} + + + {{ . }} + + {{ end }} +
+ + + {{ with $hero.highlights }} +
+ {{ range . }} + + {{ .label }} + + {{ end }} +
+ {{ end }} + + +
+ {{ with $hero.primary }} + + {{ .label | default "View projects" }} + + {{ end }} + + {{ with $hero.secondary }} + + {{ .label | default "Read the blog" }} + + {{ end }} +
+
+ + + {{ with $hero.avatar }} +
+
+
+ {{ $hero.title | default $.Site.Title }} +
+
+
+ {{ end }} +
diff --git a/themes/minimal-black/layouts/partials/home/now.html b/themes/minimal-black/layouts/partials/home/now.html new file mode 100644 index 0000000..2aa3809 --- /dev/null +++ b/themes/minimal-black/layouts/partials/home/now.html @@ -0,0 +1,28 @@ +{{- $hero := .Site.Params.hero -}} +{{- $home := .Site.Params.home -}} + +{{ if $home.showNowSection }} +
+
+

{{ default "Now" $hero.nowLabel }}

+
+ {{ with $hero.nowIntro }} +

{{ . }}

+ {{ end }} + + {{ with $hero.now }} +
    + {{ range . }} +
  • {{ . }}
  • + {{ end }} +
+ {{ else }} +

+ Add a hero.now list in your config to describe what you’re + focused on right now. +

+ {{ end }} +
+
+
+{{ end }} diff --git a/themes/minimal-black/layouts/partials/home/posts.html b/themes/minimal-black/layouts/partials/home/posts.html new file mode 100644 index 0000000..1c3b35c --- /dev/null +++ b/themes/minimal-black/layouts/partials/home/posts.html @@ -0,0 +1,38 @@ +{{- $home := .Site.Params.home -}} +{{ if ne $home.showLatestPosts false }} +
+
+

+ {{ default "Latest writing" $home.blogTitle }} +

+ {{ with $home.blogSubtitle }} +

+ {{ . }} +

+ {{ end }} +
+ + {{ $limit := cond (gt (int $home.latestPostsLimit) 0) (int $home.latestPostsLimit) 3 }} + {{ $posts := first $limit (where .Site.RegularPages "Section" "blog") }} + +
+ {{ range $posts }} + {{ partial "components/post-card.html" (dict "Page" . "Root" $) }} + {{ else }} +

+ No posts yet. Add some under content/blog. +

+ {{ end }} +
+ + +
+{{ end }} diff --git a/themes/minimal-black/layouts/partials/home/projects.html b/themes/minimal-black/layouts/partials/home/projects.html new file mode 100644 index 0000000..bb3e5b7 --- /dev/null +++ b/themes/minimal-black/layouts/partials/home/projects.html @@ -0,0 +1,43 @@ +{{- $home := .Site.Params.home -}} +{{ if ne $home.showFeaturedProjects false }} +
+
+

+ {{ default "Selected work" $home.projectsTitle }} +

+ {{ with $home.projectsSubtitle }} +

+ {{ . }} +

+ {{ end }} +
+ + {{ $limit := cond (gt (int $home.featuredProjectsLimit) 0) (int $home.featuredProjectsLimit) 3 }} + {{ $allProjects := where .Site.RegularPages "Section" "projects" }} + {{ $featured := where $allProjects "Params.featured" true }} + {{ if not (gt (len $featured) 0) }} + {{ $featured = $allProjects }} + {{ end }} + {{ $list := first $limit $featured }} + +
+ {{ range $list }} + {{ partial "components/project-card.html" (dict "Page" . "Root" $) }} + {{ else }} +

+ No projects yet. Add some under content/projects. +

+ {{ end }} +
+ + +
+{{ end }} diff --git a/themes/minimal-black/layouts/partials/home/tech-marquee.html b/themes/minimal-black/layouts/partials/home/tech-marquee.html new file mode 100644 index 0000000..588ee82 --- /dev/null +++ b/themes/minimal-black/layouts/partials/home/tech-marquee.html @@ -0,0 +1,59 @@ +{{- $home := .Site.Params.home -}} +{{- $hero := .Site.Params.hero -}} +{{- $techMain := $home.tech -}} +{{- $techReverse := $home.techReverse -}} +{{- $variant := $home.techVariant | default "wide" -}} + +{{ with $techMain }} +
+
+

+ {{ default "What I Work With" $hero.techMarqueeLabel }} +

+ +
+ +
+ {{ range . }} +
+ {{ with .icon }} + + {{ end }} + {{ .label }} +
+ {{ end }} + {{ range . }} + + {{ end }} +
+ + + {{ with $techReverse }} +
+ {{ range . }} +
+ {{ with .icon }} + + {{ end }} + {{ .label }} +
+ {{ end }} + {{ range . }} + + {{ end }} +
+ {{ end }} +
+
+
+{{ end }} diff --git a/themes/minimal-black/layouts/partials/meta.html b/themes/minimal-black/layouts/partials/meta.html new file mode 100644 index 0000000..bd9813b --- /dev/null +++ b/themes/minimal-black/layouts/partials/meta.html @@ -0,0 +1,7 @@ +{{- with .Description -}} + +{{- else -}} + +{{- end }} {{- with .Site.Params.author }} + +{{- end }} diff --git a/themes/minimal-black/layouts/partials/search-overlay.html b/themes/minimal-black/layouts/partials/search-overlay.html new file mode 100644 index 0000000..89c673a --- /dev/null +++ b/themes/minimal-black/layouts/partials/search-overlay.html @@ -0,0 +1,59 @@ +
+
+ +
+
+
+ + +
+ +
+ +
+
+
+
+ +
+

Start searching

+

+ Enter keywords to search articles. +

+
+
+ + +
+
+
diff --git a/themes/minimal-black/layouts/projects/list.html b/themes/minimal-black/layouts/projects/list.html new file mode 100644 index 0000000..5fb2d97 --- /dev/null +++ b/themes/minimal-black/layouts/projects/list.html @@ -0,0 +1,50 @@ +{{ define "main" }} +
+
+
+

Projects

+ + {{ with .Site.Params.projectsIntro }} +

+ {{ . }} +

+ {{ else }} +

+ A selection of things I’ve been building – themes, tools, and experiments. +

+ {{ end }} +
+ + + {{/* you can tweak sorting if you want; this is newest first */}} + {{ $paginator := .Paginate (.Pages.ByDate.Reverse) }} + +
+ {{ range $paginator.Pages }} + {{ partial "components/project-card.html" (dict "Page" . "Root" $) }} + {{ end }} +
+ + + {{ if gt $paginator.TotalPages 1 }} + + {{ end }} +
+
+{{ end }} diff --git a/themes/minimal-black/netlify.toml b/themes/minimal-black/netlify.toml new file mode 100644 index 0000000..82a6db3 --- /dev/null +++ b/themes/minimal-black/netlify.toml @@ -0,0 +1,29 @@ +[build] + publish = "public" + command = "mkdir -p hugo-minimal-black && rsync -av --exclude='hugo-minimal-black' --exclude='.git' --exclude='public' . ./hugo-minimal-black/ && cd hugo-minimal-black/exampleSite && hugo --gc --minify --themesDir ../.. --theme hugo-minimal-black && mv public ../../public" + +[build.environment] + HUGO_VERSION = "0.152.2" + HUGO_ENV = "production" + HUGO_ENABLEGITINFO = "true" + +[context.production.environment] + HUGO_VERSION = "0.152.2" + HUGO_ENV = "production" + +[context.deploy-preview] + command = "mkdir -p hugo-minimal-black && rsync -av --exclude='hugo-minimal-black' --exclude='.git' --exclude='public' . ./hugo-minimal-black/ && cd hugo-minimal-black/exampleSite && hugo --gc --minify --buildFuture --themesDir ../.. --theme hugo-minimal-black && mv public ../../public" + +[context.deploy-preview.environment] + HUGO_VERSION = "0.152.2" + +[context.branch-deploy] + command = "mkdir -p hugo-minimal-black && rsync -av --exclude='hugo-minimal-black' --exclude='.git' --exclude='public' . ./hugo-minimal-black/ && cd hugo-minimal-black/exampleSite && hugo --themesDir ../.. --theme hugo-minimal-black && mv public ../../public" + +[context.branch-deploy.environment] + HUGO_VERSION = "0.152.2" + +[[redirects]] + from = "/*" + to = "/404.html" + status = 404 diff --git a/themes/minimal-black/package.json b/themes/minimal-black/package.json new file mode 100644 index 0000000..90293f5 --- /dev/null +++ b/themes/minimal-black/package.json @@ -0,0 +1,19 @@ +{ + "name": "minimal-black", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "Jim Christopoulos", + "license": "ISC", + "type": "commonjs", + "devDependencies": { + "@tailwindcss/typography": "^0.5.19", + "autoprefixer": "^10.4.22", + "postcss": "^8.5.6", + "tailwindcss": "^3.4.18" + } +} diff --git a/themes/minimal-black/postcss.config.js b/themes/minimal-black/postcss.config.js new file mode 100644 index 0000000..12a703d --- /dev/null +++ b/themes/minimal-black/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/themes/minimal-black/static/css/main.css b/themes/minimal-black/static/css/main.css new file mode 100644 index 0000000..e6f9ff4 --- /dev/null +++ b/themes/minimal-black/static/css/main.css @@ -0,0 +1,5434 @@ +/* ========================================================================== + MINIMAL BLACK THEME - MAIN STYLESHEET + A minimal, dark-mode first Hugo theme + ========================================================================== */ + +/* Base Styles & Tailwind */ + +/* ========================================================================== + BASE STYLES + Tailwind imports, CSS variables, theme colors, and base styles + ========================================================================== */ + +*, ::before, ::after { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-gradient-from-position: ; + --tw-gradient-via-position: ; + --tw-gradient-to-position: ; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgb(59 130 246 / 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; + --tw-contain-size: ; + --tw-contain-layout: ; + --tw-contain-paint: ; + --tw-contain-style: ; +} + +::backdrop { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-gradient-from-position: ; + --tw-gradient-via-position: ; + --tw-gradient-to-position: ; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgb(59 130 246 / 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; + --tw-contain-size: ; + --tw-contain-layout: ; + --tw-contain-paint: ; + --tw-contain-style: ; +} + +/* ! tailwindcss v3.4.18 | MIT License | https://tailwindcss.com */ + +/* +1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4) +2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116) +*/ + +*, +::before, +::after { + box-sizing: border-box; + /* 1 */ + border-width: 0; + /* 2 */ + border-style: solid; + /* 2 */ + border-color: #e5e7eb; + /* 2 */ +} + +::before, +::after { + --tw-content: ''; +} + +/* +1. Use a consistent sensible line-height in all browsers. +2. Prevent adjustments of font size after orientation changes in iOS. +3. Use a more readable tab size. +4. Use the user's configured `sans` font-family by default. +5. Use the user's configured `sans` font-feature-settings by default. +6. Use the user's configured `sans` font-variation-settings by default. +7. Disable tap highlights on iOS +*/ + +html, +:host { + line-height: 1.5; + /* 1 */ + -webkit-text-size-adjust: 100%; + /* 2 */ + -moz-tab-size: 4; + /* 3 */ + -o-tab-size: 4; + tab-size: 4; + /* 3 */ + font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + /* 4 */ + font-feature-settings: normal; + /* 5 */ + font-variation-settings: normal; + /* 6 */ + -webkit-tap-highlight-color: transparent; + /* 7 */ +} + +/* +1. Remove the margin in all browsers. +2. Inherit line-height from `html` so users can set them as a class directly on the `html` element. +*/ + +body { + margin: 0; + /* 1 */ + line-height: inherit; + /* 2 */ +} + +/* +1. Add the correct height in Firefox. +2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) +3. Ensure horizontal rules are visible by default. +*/ + +hr { + height: 0; + /* 1 */ + color: inherit; + /* 2 */ + border-top-width: 1px; + /* 3 */ +} + +/* +Add the correct text decoration in Chrome, Edge, and Safari. +*/ + +abbr:where([title]) { + -webkit-text-decoration: underline dotted; + text-decoration: underline dotted; +} + +/* +Remove the default font size and weight for headings. +*/ + +h1, +h2, +h3, +h4, +h5, +h6 { + font-size: inherit; + font-weight: inherit; +} + +/* +Reset links to optimize for opt-in styling instead of opt-out. +*/ + +a { + color: inherit; + text-decoration: inherit; +} + +/* +Add the correct font weight in Edge and Safari. +*/ + +b, +strong { + font-weight: bolder; +} + +/* +1. Use the user's configured `mono` font-family by default. +2. Use the user's configured `mono` font-feature-settings by default. +3. Use the user's configured `mono` font-variation-settings by default. +4. Correct the odd `em` font sizing in all browsers. +*/ + +code, +kbd, +samp, +pre { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + /* 1 */ + font-feature-settings: normal; + /* 2 */ + font-variation-settings: normal; + /* 3 */ + font-size: 1em; + /* 4 */ +} + +/* +Add the correct font size in all browsers. +*/ + +small { + font-size: 80%; +} + +/* +Prevent `sub` and `sup` elements from affecting the line height in all browsers. +*/ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +/* +1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) +2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) +3. Remove gaps between table borders by default. +*/ + +table { + text-indent: 0; + /* 1 */ + border-color: inherit; + /* 2 */ + border-collapse: collapse; + /* 3 */ +} + +/* +1. Change the font styles in all browsers. +2. Remove the margin in Firefox and Safari. +3. Remove default padding in all browsers. +*/ + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; + /* 1 */ + font-feature-settings: inherit; + /* 1 */ + font-variation-settings: inherit; + /* 1 */ + font-size: 100%; + /* 1 */ + font-weight: inherit; + /* 1 */ + line-height: inherit; + /* 1 */ + letter-spacing: inherit; + /* 1 */ + color: inherit; + /* 1 */ + margin: 0; + /* 2 */ + padding: 0; + /* 3 */ +} + +/* +Remove the inheritance of text transform in Edge and Firefox. +*/ + +button, +select { + text-transform: none; +} + +/* +1. Correct the inability to style clickable types in iOS and Safari. +2. Remove default button styles. +*/ + +button, +input:where([type='button']), +input:where([type='reset']), +input:where([type='submit']) { + -webkit-appearance: button; + /* 1 */ + background-color: transparent; + /* 2 */ + background-image: none; + /* 2 */ +} + +/* +Use the modern Firefox focus style for all focusable elements. +*/ + +:-moz-focusring { + outline: auto; +} + +/* +Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737) +*/ + +:-moz-ui-invalid { + box-shadow: none; +} + +/* +Add the correct vertical alignment in Chrome and Firefox. +*/ + +progress { + vertical-align: baseline; +} + +/* +Correct the cursor style of increment and decrement buttons in Safari. +*/ + +::-webkit-inner-spin-button, +::-webkit-outer-spin-button { + height: auto; +} + +/* +1. Correct the odd appearance in Chrome and Safari. +2. Correct the outline style in Safari. +*/ + +[type='search'] { + -webkit-appearance: textfield; + /* 1 */ + outline-offset: -2px; + /* 2 */ +} + +/* +Remove the inner padding in Chrome and Safari on macOS. +*/ + +::-webkit-search-decoration { + -webkit-appearance: none; +} + +/* +1. Correct the inability to style clickable types in iOS and Safari. +2. Change font properties to `inherit` in Safari. +*/ + +::-webkit-file-upload-button { + -webkit-appearance: button; + /* 1 */ + font: inherit; + /* 2 */ +} + +/* +Add the correct display in Chrome and Safari. +*/ + +summary { + display: list-item; +} + +/* +Removes the default spacing and border for appropriate elements. +*/ + +blockquote, +dl, +dd, +h1, +h2, +h3, +h4, +h5, +h6, +hr, +figure, +p, +pre { + margin: 0; +} + +fieldset { + margin: 0; + padding: 0; +} + +legend { + padding: 0; +} + +ol, +ul, +menu { + list-style: none; + margin: 0; + padding: 0; +} + +/* +Reset default styling for dialogs. +*/ + +dialog { + padding: 0; +} + +/* +Prevent resizing textareas horizontally by default. +*/ + +textarea { + resize: vertical; +} + +/* +1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300) +2. Set the default placeholder color to the user's configured gray 400 color. +*/ + +input::-moz-placeholder, textarea::-moz-placeholder { + opacity: 1; + /* 1 */ + color: #9ca3af; + /* 2 */ +} + +input::placeholder, +textarea::placeholder { + opacity: 1; + /* 1 */ + color: #9ca3af; + /* 2 */ +} + +/* +Set the default cursor for buttons. +*/ + +button, +[role="button"] { + cursor: pointer; +} + +/* +Make sure disabled buttons don't get the pointer cursor. +*/ + +:disabled { + cursor: default; +} + +/* +1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14) +2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210) + This can trigger a poorly considered lint error in some tools but is included by design. +*/ + +img, +svg, +video, +canvas, +audio, +iframe, +embed, +object { + display: block; + /* 1 */ + vertical-align: middle; + /* 2 */ +} + +/* +Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14) +*/ + +img, +video { + max-width: 100%; + height: auto; +} + +/* Make elements with the HTML hidden attribute stay hidden by default */ + +[hidden]:where(:not([hidden="until-found"])) { + display: none; +} + +.container { + width: 100%; +} + +@media (min-width: 640px) { + .container { + max-width: 640px; + } +} + +@media (min-width: 768px) { + .container { + max-width: 768px; + } +} + +@media (min-width: 1024px) { + .container { + max-width: 1024px; + } +} + +@media (min-width: 1280px) { + .container { + max-width: 1280px; + } +} + +@media (min-width: 1536px) { + .container { + max-width: 1536px; + } +} + +.dock { + position: fixed; + right: 1.5rem; + bottom: 1.6rem; + z-index: 50; +} + +.dock-inner { + display: inline-flex; + align-items: center; + gap: 0.45rem; + padding: 0.3rem 0.5rem; + border-radius: 9999px; + /* border: 1px solid var(--color-border); + background-color: color-mix(in srgb, var(--color-surface) 92%, transparent); + box-shadow: 0 14px 40px rgba(0, 0, 0, 0.55); */ + backdrop-filter: blur(16px); +} + +.dock-panel { + padding: 0.25rem; + display: inline-flex; + align-items: center; + gap: 0.35rem; + max-width: 0; + opacity: 0; + transform: translateX(6px); + overflow: hidden; + transition: max-width 0.18s ease-out, opacity 0.18s ease-out, + transform 0.18s ease-out; +} + +.dock--open .dock-panel { + max-width: 10rem; + opacity: 1; + transform: translateX(0); +} + +.dock-divider { + width: 1px; + height: 1.4rem; + background: linear-gradient( + to bottom, + transparent, + color-mix(in srgb, var(--color-border) 80%, transparent), + transparent + ); +} + +.dock-action { + display: flex; + align-items: center; + justify-content: center; + width: 2rem; + height: 2rem; + border-radius: 9999px; + border: 1px solid var(--color-border); + background-color: color-mix(in srgb, var(--color-bg) 75%, transparent); + color: var(--color-text-muted); + cursor: pointer; + transition: background-color 0.16s ease-out, border-color 0.16s ease-out, + color 0.16s ease-out, transform 0.16s ease-out, box-shadow 0.16s ease-out; +} + +.dock-action:hover { + border-color: var(--color-accent); + color: var(--color-accent); + transform: translateY(-1px); + /* box-shadow: 0 8px 26px rgba(0, 0, 0, 0.55); */ +} + +.dock-toggle { + display: flex; + align-items: center; + justify-content: center; + width: 2.1rem; + height: 2.1rem; + border-radius: 9999px; + border: 1px solid var(--color-border); + background-color: color-mix(in srgb, var(--color-bg) 85%, transparent); + color: var(--color-text-muted); + cursor: pointer; + transition: background-color 0.16s ease-out, border-color 0.16s ease-out, + transform 0.16s ease-out, box-shadow 0.16s ease-out; +} + +.dock-toggle:hover { + border-color: var(--color-accent); + color: var(--color-accent); + transform: translateY(-1px); + box-shadow: 0 10px 24px rgba(0, 0, 0, 0.5); +} + +.dock-toggle-dots { + position: relative; + display: inline-block; + width: 0.8rem; + height: 0.16rem; + border-radius: 999px; + background-color: var(--color-text-muted); +} + +.dock-toggle-dots::before, + .dock-toggle-dots::after { + content: ""; + position: absolute; + inset-y: 0; + width: 0.16rem; + border-radius: 999px; + background-color: var(--color-text-muted); +} + +.dock-toggle-dots::before { + left: -0.18rem; +} + +.dock-toggle-dots::after { + right: -0.18rem; +} + +.dock--open .dock-toggle-dots, + .dock--open .dock-toggle-dots::before, + .dock--open .dock-toggle-dots::after { + background-color: var(--color-accent); +} + +.btn-primary-sm { + font-size: 0.75rem; + padding: 0.35rem 0.9rem; + border-radius: 999px; +} + +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; +} + +.visible { + visibility: visible; +} + +.invisible { + visibility: hidden; +} + +.collapse { + visibility: collapse; +} + +.static { + position: static; +} + +.fixed { + position: fixed; +} + +.absolute { + position: absolute; +} + +.sticky { + position: sticky; +} + +.order-first { + order: -9999; +} + +.mx-auto { + margin-left: auto; + margin-right: auto; +} + +.mb-2 { + margin-bottom: 0.5rem; +} + +.mb-8 { + margin-bottom: 2rem; +} + +.ml-1 { + margin-left: 0.25rem; +} + +.mt-0\.5 { + margin-top: 0.125rem; +} + +.mt-1 { + margin-top: 0.25rem; +} + +.mt-2 { + margin-top: 0.5rem; +} + +.mt-3 { + margin-top: 0.75rem; +} + +.mt-4 { + margin-top: 1rem; +} + +.mt-6 { + margin-top: 1.5rem; +} + +.block { + display: block; +} + +.inline { + display: inline; +} + +.flex { + display: flex; +} + +.inline-flex { + display: inline-flex; +} + +.table { + display: table; +} + +.grid { + display: grid; +} + +.contents { + display: contents; +} + +.hidden { + display: none; +} + +.h-1\.5 { + height: 0.375rem; +} + +.h-24 { + height: 6rem; +} + +.h-4 { + height: 1rem; +} + +.h-8 { + height: 2rem; +} + +.h-full { + height: 100%; +} + +.min-h-screen { + min-height: 100vh; +} + +.w-1\.5 { + width: 0.375rem; +} + +.w-24 { + width: 6rem; +} + +.w-4 { + width: 1rem; +} + +.w-8 { + width: 2rem; +} + +.w-full { + width: 100%; +} + +.min-w-0 { + min-width: 0px; +} + +.max-w-7xl { + max-width: 80rem; +} + +.max-w-xl { + max-width: 36rem; +} + +.flex-1 { + flex: 1 1 0%; +} + +.flex-shrink { + flex-shrink: 1; +} + +.transform { + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.list-disc { + list-style-type: disc; +} + +.flex-col { + flex-direction: column; +} + +.flex-wrap { + flex-wrap: wrap; +} + +.items-start { + align-items: flex-start; +} + +.items-center { + align-items: center; +} + +.items-baseline { + align-items: baseline; +} + +.justify-end { + justify-content: flex-end; +} + +.justify-center { + justify-content: center; +} + +.justify-between { + justify-content: space-between; +} + +.gap-1 { + gap: 0.25rem; +} + +.gap-1\.5 { + gap: 0.375rem; +} + +.gap-2 { + gap: 0.5rem; +} + +.gap-3 { + gap: 0.75rem; +} + +.gap-4 { + gap: 1rem; +} + +.gap-5 { + gap: 1.25rem; +} + +.gap-8 { + gap: 2rem; +} + +.space-y-1 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0.25rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0.25rem * var(--tw-space-y-reverse)); +} + +.space-y-2 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0.5rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0.5rem * var(--tw-space-y-reverse)); +} + +.space-y-3 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0.75rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0.75rem * var(--tw-space-y-reverse)); +} + +.space-y-4 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(1rem * var(--tw-space-y-reverse)); +} + +.space-y-5 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(1.25rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(1.25rem * var(--tw-space-y-reverse)); +} + +.space-y-6 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(1.5rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(1.5rem * var(--tw-space-y-reverse)); +} + +.overflow-hidden { + overflow: hidden; +} + +.truncate { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.rounded-2xl { + border-radius: 1rem; +} + +.rounded-full { + border-radius: 9999px; +} + +.border { + border-width: 1px; +} + +.border-b { + border-bottom-width: 1px; +} + +.border-border { + border-color: var(--color-border); +} + +.bg-bg { + background-color: var(--color-bg); +} + +.bg-surface { + background-color: var(--color-surface); +} + +.bg-white\/95 { + background-color: rgb(255 255 255 / 0.95); +} + +.object-cover { + -o-object-fit: cover; + object-fit: cover; +} + +.p-2 { + padding: 0.5rem; +} + +.px-3 { + padding-left: 0.75rem; + padding-right: 0.75rem; +} + +.px-4 { + padding-left: 1rem; + padding-right: 1rem; +} + +.py-1 { + padding-top: 0.25rem; + padding-bottom: 0.25rem; +} + +.py-1\.5 { + padding-top: 0.375rem; + padding-bottom: 0.375rem; +} + +.py-3 { + padding-top: 0.75rem; + padding-bottom: 0.75rem; +} + +.pb-6 { + padding-bottom: 1.5rem; +} + +.pl-4 { + padding-left: 1rem; +} + +.pt-10 { + padding-top: 2.5rem; +} + +.pt-6 { + padding-top: 1.5rem; +} + +.text-2xl { + font-size: 1.5rem; + line-height: 2rem; +} + +.text-3xl { + font-size: 1.875rem; + line-height: 2.25rem; +} + +.text-\[0\.68rem\] { + font-size: 0.68rem; +} + +.text-\[0\.72rem\] { + font-size: 0.72rem; +} + +.text-\[0\.75rem\] { + font-size: 0.75rem; +} + +.text-\[0\.7rem\] { + font-size: 0.7rem; +} + +.text-\[0\.8rem\] { + font-size: 0.8rem; +} + +.text-\[1rem\] { + font-size: 1rem; +} + +.text-base { + font-size: 1rem; + line-height: 1.5rem; +} + +.text-lg { + font-size: 1.125rem; + line-height: 1.75rem; +} + +.text-sm { + font-size: 0.875rem; + line-height: 1.25rem; +} + +.text-xs { + font-size: 0.75rem; + line-height: 1rem; +} + +.font-medium { + font-weight: 500; +} + +.font-semibold { + font-weight: 600; +} + +.italic { + font-style: italic; +} + +.tracking-tight { + letter-spacing: -0.025em; +} + +.tracking-wide { + letter-spacing: 0.025em; +} + +.text-accent { + color: var(--color-accent); +} + +.text-muted { + color: var(--color-text-muted); +} + +.text-text { + color: var(--color-text); +} + +.underline { + text-decoration-line: underline; +} + +.antialiased { + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.shadow-md { + --tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); + --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + +.shadow-sm { + --tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); + --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + +.blur { + --tw-blur: blur(8px); + filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); +} + +.filter { + filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); +} + +.backdrop-filter { + backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); +} + +.transition { + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} + +/* Theme Variables */ + +:root { + /* LIGHT THEME */ + --color-bg: #f9fafb; + --color-surface: #ffffff; + --color-text: #111827; + --color-text-muted: #6b7280; + --color-border: #e5e7eb; + --color-accent: #a855f7; +} + +html[data-theme="dark"] { + /* DARK THEME */ + --color-bg: #000000; + /* TRUE BLACK */ + --color-surface: #0a0a0a; + --color-text: #f9fafb; + --color-text-muted: #9ca3af; + --color-border: #27272a; + --color-accent: #c084fc; +} + +/* Base Styles */ + +body { + background: var(--color-bg); + color: var(--color-text); +} + +/* Utility Classes */ + +/* ========================================================================== + UTILITY CLASSES + Color utilities, animations, helper classes + ========================================================================== */ + +.bg-bg { + background-color: var(--color-bg); +} + +.bg-surface { + background-color: var(--color-surface); +} + +.text-text { + color: var(--color-text); +} + +.text-muted { + color: var(--color-text-muted); +} + +.text-accent { + color: var(--color-accent); +} + +.border-border { + border-color: var(--color-border); +} + +.border-accent { + border-color: var(--color-accent); +} + +.bg-accent { + background-color: var(--color-accent); +} + +.underline-accent { + text-decoration-color: var(--color-accent); +} + +/* Simple fade-up animation */ + +@keyframes fade-up { + 0% { + opacity: 0; + transform: translateY(8px); + } + + 100% { + opacity: 1; + transform: translateY(0); + } +} + +.animate-fade-up { + animation: fade-up 0.45s ease-out both; +} + +@keyframes search-panel-in { + 0% { + opacity: 0; + transform: translate(-50%, -48%) scale(0.97); + } + + 100% { + opacity: 1; + transform: translate(-50%, -50%) scale(1); + } +} + +@keyframes search-backdrop-in { + 0% { + opacity: 0; + } + + 100% { + opacity: 1; + } +} + +@keyframes search-panel-out { + 0% { + opacity: 1; + transform: translate(-50%, -50%) scale(1); + } + + 100% { + opacity: 0; + transform: translate(-50%, -48%) scale(0.97); + } +} + +@keyframes search-backdrop-out { + 0% { + opacity: 1; + } + + 100% { + opacity: 0; + } +} + +/* Layout helpers */ + +/* Components */ + +/* ========================================================================== + DOCK COMPONENT + Floating action dock with toggle and panel + ========================================================================== */ + +/* ========================================================================== + CARD COMPONENTS + Home cards, project cards, post cards, CTA cards, badges + ========================================================================== */ + +.card-home { + display: flex; + flex-direction: column; + justify-content: space-between; + gap: 0.5rem; + background-color: color-mix(in srgb, var(--color-surface) 96%, transparent); + box-shadow: 0 18px 40px rgba(0, 0, 0, 0.55); +} + +.card-home--project { + position: relative; + overflow: hidden; + transition: + transform 0.18s ease-out, + box-shadow 0.18s ease-out, + border-color 0.18s ease-out, + background-color 0.18s ease-out; +} + +.card-home--project::after { + content: ""; + position: absolute; + inset: -40%; + background: radial-gradient( + circle at 120% 50%, + color-mix(in srgb, var(--color-accent) 70%, transparent), + transparent 60% + ); + opacity: 0; + transform: translateX(-8px); + transition: + opacity 0.22s ease-out, + transform 0.22s ease-out; + pointer-events: none; +} + +.card-home--project:hover { + transform: translateY(-4px) translateX(3px); + border-color: color-mix(in srgb, var(--color-accent) 40%, var(--color-border)); + box-shadow: + 0 18px 40px rgba(0, 0, 0, 0.7), + 16px 0 38px rgba(124, 58, 237, 0.45); + /* purple-ish accent on right */ + background-color: color-mix(in srgb, var(--color-surface) 99%, transparent); +} + +.card-home--project:hover::after { + opacity: 0.22; + transform: translateX(0); +} + +.card-home-body { + display: flex; + flex-direction: column; + gap: 0.5rem; + text-decoration: none; + color: inherit; +} + +.card-home--post { + background-color: color-mix(in srgb, var(--color-surface) 94%, transparent); +} + +.card-home-header { + display: flex; + align-items: flex-start; + gap: 0.6rem; +} + +.card-home-icon { + display: inline-flex; + align-items: center; + justify-content: center; + width: 2.1rem; + height: 2.1rem; + border-radius: 999px; + border: 1px solid var(--color-border); + background-color: color-mix(in srgb, var(--color-bg) 92%, transparent); + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4); + font-size: 0.85rem; +} + +.card-badge { + margin-left: 0.25rem; + padding: 0.05rem 0.35rem; + border-radius: 999px; + background-color: color-mix(in srgb, var(--color-accent) 22%, transparent); + color: var(--color-accent); + font-size: 0.6rem; + font-weight: 600; +} + +.card-home-footer { + margin-top: 0.5rem; + display: flex; + align-items: center; + gap: 0.5rem; + justify-content: space-between; +} + +.card-home-footer--buttons { + padding-top: 0.4rem; + border-top: 1px solid color-mix(in srgb, var(--color-border) 80%, transparent); +} + +.card-tag-row { + display: flex; + flex-wrap: wrap; + gap: 0.25rem; +} + +.card-tag-pill { + padding: 0.1rem 0.45rem; + border-radius: 999px; + border: 1px solid color-mix(in srgb, var(--color-border) 80%, transparent); + background-color: color-mix(in srgb, var(--color-bg) 92%, transparent); + font-size: 0.65rem; + color: var(--color-text-muted); +} + +.card-cta { + display: inline-flex; + align-items: center; + gap: 0.35rem; + font-size: 0.7rem; + color: var(--color-text-muted); + text-decoration: none; + padding: 0.25rem 0.6rem; + border-radius: 999px; + border: 1px solid transparent; + transition: + color 0.15s ease-out, + border-color 0.15s ease-out, + background-color 0.15s ease-out, + transform 0.15s ease-out; +} + +.card-cta-btn { + display: inline-flex; + align-items: center; + gap: 0.35rem; + padding: 0.25rem 0.6rem; + border-radius: 8px; + font-size: 0.68rem; + color: var(--color-text-muted); + background-color: color-mix(in srgb, var(--color-bg) 92%, transparent); + border: 1px solid color-mix(in srgb, var(--color-border) 80%, transparent); + text-decoration: none; + transition: + color 0.15s ease-out, + background-color 0.15s ease-out, + border-color 0.15s ease-out, + transform 0.15s ease-out, + box-shadow 0.15s ease-out; +} + +.card-cta-btn:hover { + color: var(--color-accent); + background-color: color-mix(in srgb, var(--color-surface) 96%, transparent); + border-color: color-mix(in srgb, var(--color-accent) 50%, transparent); + transform: translateY(-1px); + box-shadow: 0 10px 28px rgba(0, 0, 0, 0.45); +} + +.card-cta-repo { + display: inline-flex; + align-items: center; + gap: 0.4rem; + padding: 0.25rem 0.7rem; + border-radius: 999px; + border: 1px solid color-mix(in srgb, var(--color-border) 85%, transparent); + background-color: color-mix(in srgb, var(--color-bg) 94%, transparent); + font-size: 0.7rem; + color: var(--color-text-muted); + text-decoration: none; + transition: + color 0.15s ease-out, + border-color 0.15s ease-out, + background-color 0.15s ease-out, + transform 0.15s ease-out, + box-shadow 0.15s ease-out; +} + +.card-cta-repo:hover { + color: var(--color-accent); + border-color: color-mix(in srgb, var(--color-accent) 55%, transparent); + background-color: color-mix(in srgb, var(--color-surface) 96%, transparent); + transform: translateY(-1px); + box-shadow: 0 10px 28px rgba(0, 0, 0, 0.5); +} + +.card-cta:hover { + color: var(--color-accent); + border-color: color-mix(in srgb, var(--color-accent) 40%, transparent); + background-color: color-mix(in srgb, var(--color-surface) 94%, transparent); + transform: translateY(-1px); +} + +.card-home--post { + position: relative; + overflow: hidden; + background-color: color-mix(in srgb, var(--color-surface) 94%, transparent); + border-left-width: 2px; + border-left-color: color-mix(in srgb, var(--color-border) 80%, transparent); + transition: + transform 0.18s ease-out, + box-shadow 0.18s ease-out, + border-color 0.18s ease-out, + background-color 0.18s ease-out; +} + +.card-home--post::before { + content: ""; + position: absolute; + left: -4px; + top: -20%; + bottom: -20%; + width: 10px; + background: linear-gradient( + to bottom, + transparent, + color-mix(in srgb, var(--color-accent) 70%, transparent), + transparent + ); + opacity: 0; + transform: translateX(-6px); + transition: + opacity 0.22s ease-out, + transform 0.22s ease-out; + pointer-events: none; +} + +.card-home--post:hover { + transform: translateY(-3px); + background-color: color-mix(in srgb, var(--color-surface) 98%, transparent); + border-left-color: color-mix(in srgb, var(--color-accent) 55%, var(--color-border)); + box-shadow: 0 16px 34px rgba(0, 0, 0, 0.65); +} + +.card-home--post:hover::before { + opacity: 0.55; + transform: translateX(0); +} + +.card-home-icon--post { + background-color: color-mix(in srgb, var(--color-bg) 94%, transparent); +} + +.card-badge--soft { + background-color: color-mix(in srgb, var(--color-accent) 16%, transparent); + color: color-mix(in srgb, var(--color-accent) 85%, white); +} + +.card { + border-radius: 0.75rem; + transition: all 0.2s ease-out; +} + +.card-pad { + padding: 1rem; +} + +.card:hover { + box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2); + border-color: color-mix(in srgb, var(--color-accent) 40%, transparent); +} + +/* ========================================================================== + LAYOUT & NAVIGATION + Page layouts, headers, footers, navigation links + ========================================================================== */ + +.layout-page { + margin-left: auto; + margin-right: auto; + max-width: 80rem; + padding-left: 1rem; + padding-right: 1rem; + padding-top: 1.5rem; + padding-bottom: 1.5rem; +} + +@media (min-width: 640px) { + .layout-page { + padding-left: 1.5rem; + padding-right: 1.5rem; + } +} + +@media (min-width: 1024px) { + .layout-page { + padding-top: 3rem; + padding-bottom: 3rem; + } +} + +.layout-page-tight { + margin-left: auto; + margin-right: auto; + max-width: 72rem; + padding-left: 1rem; + padding-right: 1rem; + padding-top: 1rem; + padding-bottom: 1rem; +} + +@media (min-width: 640px) { + .layout-page-tight { + padding-left: 1.5rem; + padding-right: 1.5rem; + } +} + +@media (min-width: 1024px) { + .layout-page-tight { + padding-top: 2.5rem; + padding-bottom: 2.5rem; + } +} + +.section-stack > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(2.5rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(2.5rem * var(--tw-space-y-reverse)); +} + +.section-stack--home > * + * { + margin-top: 1.75rem; +} + +.section-stack > div + div.posts-section { + border-top: 1px solid color-mix(in srgb, var(--color-border) 60%, transparent); + padding-top: 1.5rem; +} + +/* Headings */ + +.heading-page { + font-size: 1.875rem; + line-height: 2.25rem; + font-weight: 600; + letter-spacing: -0.025em; +} + +@media (min-width: 640px) { + .heading-page { + font-size: 2.25rem; + line-height: 2.5rem; + } +} + +.heading-section { + font-size: 0.875rem; + line-height: 1.25rem; + font-weight: 600; + letter-spacing: -0.025em; +} + +.eyebrow { + font-size: 0.65rem; + text-transform: uppercase; + letter-spacing: 0.22em; +} + +/* Cards */ + +.card { + border-radius: 0.75rem; + border-width: 1px; + --tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); + --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + background-color: var(--color-surface); + border-color: var(--color-border); +} + +.card-pad { + padding: 1rem; +} + +@media (min-width: 640px) { + .card-pad { + padding: 1.25rem; + } +} + +.card-hover { + transition-property: transform; + transition-property: box-shadow; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); +} + +.card-hover:hover { + --tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); + --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + transform: translateY(-1px); + border-color: var(--color-accent); +} + +/* Buttons */ + +.btn-primary { + display: inline-flex; + align-items: center; + border-radius: 9999px; + padding-left: 1rem; + padding-right: 1rem; + padding-top: 0.5rem; + padding-bottom: 0.5rem; + font-size: 0.75rem; + line-height: 1rem; + font-weight: 500; + --tw-text-opacity: 1; + color: rgb(255 255 255 / var(--tw-text-opacity, 1)); + --tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); + --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + transition-property: transform; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + background-color: var(--color-accent); +} + +.btn-primary:hover { + transform: translateY(-1px); +} + +.btn-ghost { + display: inline-flex; + align-items: center; + font-size: 0.75rem; + line-height: 1rem; + font-weight: 500; + color: var(--color-text-muted); +} + +.nav-shell { + border-radius: 9999px; + border-width: 1px; + padding: 0.75rem; + --tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); + --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + --tw-backdrop-blur: blur(8px); + backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); + border-color: var(--color-border); + background-color: color-mix(in srgb, var(--color-surface) 82%, transparent); +} + +.nav-shell::before { + content: ""; + position: absolute; + inset-inline: 0; + top: 0; + bottom: 0; + height: 1px; + background: linear-gradient( + 90deg, + transparent, + color-mix(in srgb, var(--color-accent) 80%, transparent), + transparent + ); + opacity: 0.8; + pointer-events: none; +} + +.nav-inner { + display: flex; + align-items: center; + justify-content: space-between; + padding-left: 1rem; + padding-right: 1rem; + padding-top: 0.5rem; + padding-bottom: 0.5rem; +} + +@media (min-width: 640px) { + .nav-inner { + padding-left: 1.25rem; + padding-right: 1.25rem; + padding-top: 0.625rem; + padding-bottom: 0.625rem; + } +} + +/* Brand logo badge (fallback) */ + +.logo-badge { + display: flex; + height: 2rem; + width: 2rem; + align-items: center; + justify-content: center; + border-radius: 9999px; + font-size: 0.65rem; + font-weight: 600; + --tw-text-opacity: 1; + color: rgb(255 255 255 / var(--tw-text-opacity, 1)); + background-color: var(--color-accent); +} + +/* Nav links */ + +.nav-link { + font-size: 0.78rem; + font-weight: 500; + color: var(--color-text-muted); +} + +.nav-link:hover { + color: var(--color-accent); +} + +/* Theme toggle */ + +.theme-toggle { + display: inline-flex; + height: 2rem; + width: 2rem; + align-items: center; + justify-content: center; + border-radius: 9999px; + border-width: 1px; + font-size: 0.7rem; + --tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); + --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + background-color: var(--color-bg); + color: var(--color-text-muted); + border-color: var(--color-border); + transition: background-color 0.15s ease-out, color 0.15s ease-out, + transform 0.15s ease-out; +} + +.theme-toggle:hover { + color: var(--color-accent); + transform: translateY(-1px); +} + +html[data-theme="dark"] .theme-toggle { + background-color: var(--color-surface); +} + +/* Fancy underline link */ + +.link-underline { + position: relative; + display: inline-block; +} + +.link-underline::after { + content: ""; + position: absolute; + left: 0; + bottom: -0.15rem; + width: 0; + height: 1px; + background-color: var(--color-accent); + transition: width 0.16s ease-out; +} + +.link-underline:hover::after { + width: 100%; +} + +/* Footer shell */ + +.footer-shell { + border-radius: 1rem; + border-width: 1px; + --tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); + --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + --tw-backdrop-blur: blur(8px); + backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); + border-color: var(--color-border); + position: relative; + overflow: hidden; + background-color: color-mix(in srgb, var(--color-surface) 86%, transparent); +} + +.footer-shell::before { + content: ""; + position: absolute; + inset-inline: 0; + top: 0; + height: 1px; + background: linear-gradient( + 90deg, + transparent, + color-mix(in srgb, var(--color-accent) 80%, transparent), + transparent + ); + opacity: 0.8; + pointer-events: none; +} + +.footer-inner { + display: flex; + flex-direction: column; + gap: 0.75rem; + padding-left: 1rem; + padding-right: 1rem; + padding-top: 0.75rem; + padding-bottom: 0.75rem; + font-size: 0.72rem; +} + +@media (min-width: 640px) { + .footer-inner { + flex-direction: row; + align-items: center; + justify-content: space-between; + padding-left: 1.25rem; + padding-right: 1.25rem; + } +} + +.footer-links { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0.75rem; +} + +.footer-link { + display: inline-flex; + align-items: center; + gap: 0.25rem; + font-size: 0.75rem; + font-weight: 500; + transition-property: all; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + color: var(--color-text-muted); +} + +.footer-link:hover { + color: var(--color-accent); +} + +.footer-small { + font-size: 0.72rem; + line-height: 1.625; + color: var(--color-text-muted); +} + +.footer-float:hover { + color: var(--color-accent); + transform: translateY(-1px); +} + +.footer-float:hover i { + filter: drop-shadow( + 0 0 2px color-mix(in srgb, var(--color-accent) 60%, transparent) + ); +} + +/* ========================================================================== + SEARCH OVERLAY + Search modal, search results, empty states + ========================================================================== */ + +.search-overlay { + position: fixed; + inset: 0; + z-index: 40; + display: none; + align-items: center; + justify-content: center; + opacity: 0; + pointer-events: none; + transition: opacity 0.18s ease-out; +} + +.search-overlay--open { + opacity: 1; + display: block; + pointer-events: auto; +} + +.search-overlay-backdrop { + position: absolute; + inset: 0; + background: radial-gradient( + circle at top, + rgba(0, 0, 0, 0.4), + transparent 55% + ), + rgba(0, 0, 0, 0.7); + backdrop-filter: blur(12px); +} + +.search-panel { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 100%; + max-width: 36rem; + margin-inline: 1.5rem; + border-radius: 1.1rem; + border: 1px solid var(--color-border); + background-color: color-mix(in srgb, var(--color-bg) 96%, transparent); + box-shadow: 0 26px 70px rgba(0, 0, 0, 0.85); + padding: 0.8rem 0.9rem 0.6rem; + display: flex; + flex-direction: column; + gap: 0.6rem; +} + +.search-overlay--open .search-panel { + animation: search-panel-in 0.18s ease-out; +} + +.search-overlay-backdrop { + position: absolute; + inset: 0; + background: radial-gradient( + circle at top, + rgba(0, 0, 0, 0.4), + transparent 55% + ), + rgba(0, 0, 0, 0.7); + backdrop-filter: blur(12px); +} + +.search-overlay .search-overlay--open .search-overlay-backdrop { + animation: search-backdrop-in 0.18s ease-out; +} + +.search-overlay--closing .search-panel { + animation: search-panel-out 0.18s ease-in forwards; +} + +.search-overlay--closing .search-overlay-backdrop { + animation: search-backdrop-out 0.18s ease-in forwards; +} + +.search-panel-header { + display: flex; + align-items: center; + gap: 0.6rem; +} + +.search-input-wrap { + flex: 1; + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.45rem 0.8rem; + border-radius: 999px; + border: 1px solid var(--color-border); + background-color: color-mix(in srgb, var(--color-surface) 96%, transparent); +} + +.search-input { + flex: 1; + border: none; + outline: none; + font-size: 0.82rem; + background: transparent; + color: var(--color-text); +} + +.search-input::-moz-placeholder { + color: var(--color-text-muted); +} + +.search-input::placeholder { + color: var(--color-text-muted); +} + +.search-close { + width: 2rem; + height: 2rem; + border-radius: 999px; + border: 1px solid var(--color-border); + background-color: color-mix(in srgb, var(--color-bg) 90%, transparent); + color: var(--color-text-muted); + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + transition: background-color 0.16s ease-out, border-color 0.16s ease-out, + color 0.16s ease-out, transform 0.16s ease-out; +} + +.search-close:hover { + border-color: var(--color-accent); + color: var(--color-accent); + transform: translateY(-1px); +} + +.search-panel-body { + border-radius: 0.9rem; + border: 1px solid color-mix(in srgb, var(--color-border) 70%, transparent); + background-color: color-mix(in srgb, var(--color-surface) 96%, transparent); + padding: 0.5rem 0.4rem 0.4rem; + display: flex; + flex-direction: column; +} + +.search-results { + max-height: 18rem; + padding: 0.25rem 0.15rem 0.4rem; + overflow-y: auto; +} + +.search-empty-state { + display: flex; + flex-direction: column; + align-items: center; + padding: 1.2rem 0.5rem 1.4rem; + gap: 0.4rem; +} + +.search-empty-icon { + width: 2.3rem; + height: 2.3rem; + border-radius: 999px; + border: 1px solid var(--color-border); + background-color: color-mix(in srgb, var(--color-bg) 92%, transparent); + display: flex; + align-items: center; + justify-content: center; +} + +.search-empty-title { + font-size: 0.86rem; + font-weight: 600; + color: var(--color-text); +} + +.search-empty-subtitle { + font-size: 0.76rem; + color: var(--color-text-muted); +} + +.search-footer-hints { + display: flex; + flex-wrap: wrap; + gap: 0.45rem; + border-top: 1px solid + color-mix(in srgb, var(--color-border) 75%, transparent); + padding: 0.4rem 0.5rem 0.1rem; + margin-top: 0.1rem; +} + +.search-hint-key { + display: inline-flex; + align-items: center; + gap: 0.25rem; + font-size: 0.68rem; + color: var(--color-text-muted); +} + +.search-hint-key span:first-child, + .search-hint-key span:nth-child(2) { + padding: 0.1rem 0.3rem; + border-radius: 0.3rem; + border: 1px solid color-mix(in srgb, var(--color-border) 80%, transparent); + background-color: color-mix(in srgb, var(--color-bg) 96%, transparent); + font-size: 0.65rem; +} + +.search-hint-key.search-hint-right { + margin-left: auto; +} + +.search-result-item { + display: block; + padding: 0.42rem 0.55rem; + border-radius: 0.6rem; + text-decoration: none; + transition: background-color 0.12s ease-out, transform 0.12s ease-out; +} + +.search-result-item:hover { + background-color: color-mix(in srgb, var(--color-surface) 92%, transparent); + transform: translateY(-1px); +} + +.search-result-title { + font-size: 0.8rem; + font-weight: 500; + color: var(--color-text); +} + +.search-result-meta { + margin-top: 0.15rem; + font-size: 0.7rem; + color: var(--color-text-muted); +} + +.search-footer-hints { + display: flex; + flex-wrap: wrap; + gap: 0.45rem; + border-top: 1px solid + color-mix(in srgb, var(--color-border) 75%, transparent); + padding: 0.4rem 0.5rem 0.1rem; + margin-top: 0.1rem; +} + +.search-hint-key { + display: inline-flex; + align-items: center; + gap: 0.25rem; + font-size: 0.68rem; + color: var(--color-text-muted); +} + +.search-hint-key span:first-child, + .search-hint-key span:nth-child(2) { + padding: 0.1rem 0.3rem; + border-radius: 0.3rem; + border: 1px solid color-mix(in srgb, var(--color-border) 80%, transparent); + background-color: color-mix(in srgb, var(--color-bg) 96%, transparent); + font-size: 0.65rem; +} + +.search-hint-key.search-hint-right { + margin-left: auto; +} + +.search-result-item { + display: block; + padding: 0.42rem 0.55rem; + border-radius: 0.6rem; + text-decoration: none; + transition: background-color 0.12s ease-out, transform 0.12s ease-out; +} + +.search-result-item:hover { + background-color: color-mix(in srgb, var(--color-surface) 92%, transparent); + transform: translateY(-1px); +} + +.search-result-title { + font-size: 0.8rem; + font-weight: 500; + color: var(--color-text); +} + +.search-result-meta { + margin-top: 0.15rem; + font-size: 0.7rem; + color: var(--color-text-muted); +} + +/* ========================================================================== + TECH MARQUEE + Technology carousel/strip component + ========================================================================== */ + +.tech-carousel { + display: flex; + gap: 0.5rem; + padding: 0.3rem 0.1rem 0.1rem; + overflow-x: auto; + scroll-snap-type: x mandatory; +} + +.tech-carousel::-webkit-scrollbar { + height: 4px; +} + +.tech-carousel::-webkit-scrollbar-thumb { + border-radius: 999px; + background-color: rgba(255, 255, 255, 0.12); +} + +.tech-pill { + scroll-snap-align: start; + display: inline-flex; + align-items: center; + gap: 0.4rem; + padding: 0.35rem 0.7rem; + border-radius: 999px; + border: 1px solid var(--color-border); + background-color: color-mix(in srgb, var(--color-surface) 92%, transparent); + box-shadow: 0 10px 28px rgba(0, 0, 0, 0.28); + white-space: nowrap; +} + +.badge-available { + display: inline-flex; + align-items: center; + gap: 0.375rem; + padding: 0.25rem 0.7rem; + border-radius: 999px; + background-color: var(--color-accent); + color: white; + font-size: 0.7rem; + font-weight: 600; + box-shadow: 0 0 0.4rem rgba(0, 0, 0, 0.4); +} + +/* --- Tech marquee --- */ + +.tech-icon { + font-size: 1.5rem; +} + +.tech-strip { + position: relative; + overflow: hidden; + padding-block: 0.4rem; +} + +.tech-strip-track { + padding-top: 0.5rem; + padding-bottom: 0.5rem; + display: inline-flex; + align-items: center; + white-space: nowrap; + animation-name: tech-marquee; + animation-timing-function: linear; + animation-iteration-count: infinite; + animation-play-state: running; + gap: 1.5rem; +} + +.tech-strip--wide .tech-strip-track--primary { + animation-duration: 40s; +} + +.tech-strip--wide .tech-strip-track--secondary { + animation-duration: 40s; + animation-direction: reverse; +} + +.tech-strip--compact .tech-strip-track--primary { + animation-duration: 40s; +} + +.tech-strip--compact .tech-strip-track--secondary { + animation-duration: 48s; + animation-direction: reverse; +} + +.tech-strip--compact .tech-strip-track { + gap: 1.1rem; +} + +/* pause both rows on hover */ + +.tech-strip:hover .tech-strip-track { + animation-play-state: paused; +} + +.tech-strip-item { + display: inline-flex; + align-items: center; + gap: 0.4rem; + opacity: 0.88; + transition: opacity 0.15s ease-out, transform 0.15s ease-out; +} + +.tech-strip-item:hover { + opacity: 1; + transform: translateY(-1px); +} + +/* Content */ + +/* ========================================================================== + MARKDOWN CONTENT STYLES + Typography, blockquotes, lists, code blocks, tables, alerts + ========================================================================== */ + +.markdown-body { + max-width: 85ch; + margin-inline: auto; + font-size: 0.95rem; + line-height: 1.75; + color: var(--color-text); + overflow-wrap: break-word; + word-wrap: break-word; + min-width: 0; +} + +/* rhythm */ + +.markdown-body > * { + margin-top: 0; + margin-bottom: 0; +} + +.markdown-body > * + * { + margin-top: 1.25rem; +} + +/* paragraphs */ + +.markdown-body p { + color: var(--color-text); + line-height: 1.75; +} + +/* headings */ + +.markdown-body h1, + .markdown-body h2, + .markdown-body h3, + .markdown-body h4, + .markdown-body h5, + .markdown-body h6 { + color: var(--color-text); + font-weight: 650; + line-height: 1.3; + letter-spacing: -0.01em; + margin-top: 2rem; + margin-bottom: 0.75rem; +} + +.markdown-body h1 { + font-size: 2rem; + margin-top: 0; +} + +.markdown-body h2 { + font-size: 1.5rem; + padding-bottom: 0.4rem; + border-bottom: 1px solid color-mix(in srgb, var(--color-border) 60%, transparent); + margin-top: 2.5rem; +} + +.markdown-body h3 { + font-size: 1.25rem; + margin-top: 2rem; +} + +.markdown-body h4 { + font-size: 1.1rem; +} + +.markdown-body h5 { + font-size: 1rem; +} + +.markdown-body h6 { + font-size: 0.85rem; + text-transform: uppercase; + letter-spacing: 0.1em; + color: var(--color-text-muted); +} + +/* links */ + +.markdown-body a { + color: var(--color-accent); + text-decoration: none; + border-bottom: 1px solid color-mix(in srgb, var(--color-accent) 35%, transparent); + padding-bottom: 0.05rem; + transition: color 0.15s ease-out, border-color 0.15s ease-out; + word-wrap: break-word; + overflow-wrap: break-word; + word-break: break-word; +} + +.markdown-body a:hover { + color: color-mix(in srgb, var(--color-accent) 85%, white); + border-bottom-color: var(--color-accent); +} + +/* strong and emphasis */ + +.markdown-body strong { + font-weight: 650; + color: var(--color-text); +} + +.markdown-body em { + font-style: italic; +} + +/* lists */ + +.markdown-body ul, + .markdown-body ol { + padding-left: 1.75rem; + margin-top: 1rem; + margin-bottom: 1rem; +} + +.markdown-body li { + margin: 0.5rem 0; + line-height: 1.7; + padding-left: 0.5rem; + position: relative; + transition: background-color 0.15s ease-out; +} + +.markdown-body li:hover { + background-color: color-mix(in srgb, var(--color-surface) 30%, transparent); + border-radius: 0.35rem; +} + +.markdown-body li > ul, + .markdown-body li > ol { + margin-top: 0.5rem; + margin-bottom: 0.5rem; +} + +/* Unordered list markers */ + +.markdown-body ul li::marker { + color: var(--color-accent); + font-size: 1.1em; +} + +.markdown-body ul { + list-style-type: disc; +} + +.markdown-body ul ul { + list-style-type: circle; +} + +.markdown-body ul ul ul { + list-style-type: square; +} + +/* Ordered list markers */ + +.markdown-body ol { + list-style-type: decimal; +} + +.markdown-body ol li::marker { + color: var(--color-accent); + font-weight: 700; + font-size: 0.95em; +} + +.markdown-body ol ol { + list-style-type: lower-alpha; +} + +.markdown-body ol ol ol { + list-style-type: lower-roman; +} + +/* Better spacing for nested lists */ + +.markdown-body li > p { + margin-top: 0.35rem; + margin-bottom: 0.35rem; +} + +.markdown-body li:first-child { + margin-top: 0; +} + +.markdown-body li:last-child { + margin-bottom: 0; +} + +/* definition lists */ + +.markdown-body dl { + margin: 1.5rem 0; + padding: 1.25rem; + border-left: 3px solid var(--color-accent); + background: linear-gradient( + to right, + color-mix(in srgb, var(--color-accent) 8%, transparent), + color-mix(in srgb, var(--color-surface) 40%, transparent) 3rem + ); + border-radius: 0.6rem; +} + +.markdown-body dt { + font-weight: 650; + color: var(--color-text); + margin-top: 1rem; + margin-bottom: 0.5rem; + font-size: 1rem; + display: flex; + align-items: center; + gap: 0.5rem; +} + +.markdown-body dt:first-child { + margin-top: 0; +} + +.markdown-body dt::before { + content: '▸'; + color: var(--color-accent); + font-size: 0.9em; + font-weight: bold; +} + +.markdown-body dd { + margin-left: 1.5rem; + margin-bottom: 0.75rem; + padding-left: 1rem; + border-left: 2px solid color-mix(in srgb, var(--color-border) 60%, transparent); + color: var(--color-text-muted); + line-height: 1.7; +} + +.markdown-body dd:last-child { + margin-bottom: 0; +} + +.markdown-body dd > p { + margin: 0.25rem 0; +} + +.markdown-body dd > p:first-child { + margin-top: 0; +} + +.markdown-body dd > p:last-child { + margin-bottom: 0; +} + +/* task lists */ + +.markdown-body ul.contains-task-list { + list-style: none; + padding-left: 0; +} + +.markdown-body .task-list-item { + display: flex; + align-items: flex-start; + gap: 0.65rem; + padding: 0.5rem 0.75rem; + margin: 0.35rem 0; + border-radius: 0.45rem; + transition: all 0.15s ease-out; + background-color: color-mix(in srgb, var(--color-surface) 20%, transparent); +} + +.markdown-body .task-list-item:hover { + background-color: color-mix(in srgb, var(--color-surface) 50%, transparent); + transform: translateX(3px); +} + +.markdown-body .task-list-item input[type="checkbox"] { + margin-top: 0.35rem; + flex-shrink: 0; + width: 1.1rem; + height: 1.1rem; + cursor: pointer; + accent-color: var(--color-accent); + border-radius: 0.25rem; +} + +.markdown-body .task-list-item input[type="checkbox"]:checked { + accent-color: #22c55e; +} + +.markdown-body .task-list-item input[type="checkbox"]:checked + * { + color: var(--color-text-muted); + text-decoration: line-through; + text-decoration-color: var(--color-text-muted); +} + +/* blockquote */ + +.markdown-body blockquote, + .markdown-body .md-blockquote { + margin: 1.75rem 0; + padding: 1.25rem 1.5rem; + border-left: 4px solid var(--color-accent); + background: linear-gradient( + to right, + color-mix(in srgb, var(--color-accent) 8%, transparent), + color-mix(in srgb, var(--color-surface) 60%, transparent) 3rem + ); + border-radius: 0.6rem; + color: var(--color-text); + font-style: italic; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); + position: relative; +} + +.markdown-body blockquote::before, + .markdown-body .md-blockquote::before { + content: '"'; + position: absolute; + left: 0.75rem; + top: 0.5rem; + font-size: 2.5rem; + line-height: 1; + color: color-mix(in srgb, var(--color-accent) 25%, transparent); + font-family: Georgia, serif; + font-weight: bold; +} + +.markdown-body blockquote p, + .markdown-body .md-blockquote p { + color: var(--color-text); + font-size: 0.98rem; + line-height: 1.7; +} + +.markdown-body blockquote p + p, + .markdown-body .md-blockquote p + p { + margin-top: 0.85rem; +} + +.markdown-body blockquote strong, + .markdown-body .md-blockquote strong { + color: var(--color-text); + font-weight: 650; +} + +/* GitHub-style alerts */ + +.markdown-body .md-alert { + margin: 1.75rem 0; + padding: 1rem 1.25rem 1rem 3rem; + border-radius: 0.6rem; + border-left: 4px solid; + position: relative; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); +} + +.markdown-body .md-alert::before { + content: ''; + position: absolute; + left: 1rem; + top: 1.1rem; + width: 1.25rem; + height: 1.25rem; + background-size: contain; + background-repeat: no-repeat; + background-position: center; +} + +.markdown-body .md-alert p:first-child { + margin-top: 0; +} + +.markdown-body .md-alert p:last-child { + margin-bottom: 0; +} + +/* Note alert (blue) */ + +.markdown-body .md-alert-note { + background: linear-gradient( + to right, + color-mix(in srgb, #3b82f6 10%, transparent), + color-mix(in srgb, var(--color-surface) 60%, transparent) 3rem + ); + border-left-color: #3b82f6; +} + +.markdown-body .md-alert-note::before { + content: 'ℹ'; + display: flex; + align-items: center; + justify-content: center; + font-size: 1rem; + font-weight: bold; + color: #3b82f6; + background: none; +} + +/* Tip alert (green) */ + +.markdown-body .md-alert-tip { + background: linear-gradient( + to right, + color-mix(in srgb, #22c55e 10%, transparent), + color-mix(in srgb, var(--color-surface) 60%, transparent) 3rem + ); + border-left-color: #22c55e; +} + +.markdown-body .md-alert-tip::before { + content: '💡'; + font-size: 1.1rem; +} + +/* Important alert (purple) */ + +.markdown-body .md-alert-important { + background: linear-gradient( + to right, + color-mix(in srgb, var(--color-accent) 12%, transparent), + color-mix(in srgb, var(--color-surface) 60%, transparent) 3rem + ); + border-left-color: var(--color-accent); +} + +.markdown-body .md-alert-important::before { + content: '⚡'; + font-size: 1.1rem; +} + +/* Warning alert (yellow/orange) */ + +.markdown-body .md-alert-warning { + background: linear-gradient( + to right, + color-mix(in srgb, #f59e0b 10%, transparent), + color-mix(in srgb, var(--color-surface) 60%, transparent) 3rem + ); + border-left-color: #f59e0b; +} + +.markdown-body .md-alert-warning::before { + content: '⚠'; + display: flex; + align-items: center; + justify-content: center; + font-size: 1.1rem; + color: #f59e0b; + background: none; +} + +/* Danger/Caution alert (red) */ + +.markdown-body .md-alert-danger, + .markdown-body .md-alert-caution { + background: linear-gradient( + to right, + color-mix(in srgb, #ef4444 10%, transparent), + color-mix(in srgb, var(--color-surface) 60%, transparent) 3rem + ); + border-left-color: #ef4444; +} + +.markdown-body .md-alert-danger::before, + .markdown-body .md-alert-caution::before { + content: '🛑'; + font-size: 1.1rem; +} + +/* inline code */ + +.markdown-body :not(pre) > code { + font-family: 'JetBrains Mono', 'Fira Code', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; + font-size: 0.88em; + padding: 0.15rem 0.4rem; + border-radius: 0.35rem; + background: color-mix(in srgb, var(--color-surface) 80%, transparent); + border: 1px solid color-mix(in srgb, var(--color-border) 70%, transparent); + color: color-mix(in srgb, var(--color-accent) 90%, white); +} + +html[data-theme="light"] .markdown-body :not(pre) > code { + background: #f3f4f6; + border-color: #d1d5db; + color: #7c3aed; +} + +/* CODE BLOCKS - This is the important part */ + +.markdown-body pre { + margin: 1.5rem 0; + padding: 0; + overflow: hidden; + overflow-x: auto; + border-radius: 0.75rem; + border: 1px solid color-mix(in srgb, var(--color-border) 70%, transparent); + background: color-mix(in srgb, var(--color-surface) 40%, transparent); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); + max-width: 100%; +} + +html[data-theme="light"] .markdown-body pre { + background: #f9fafb; + border-color: #e5e7eb; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); +} + +.markdown-body pre code { + display: block; + padding: 1.25rem 1.5rem; + overflow-x: auto; + font-family: 'JetBrains Mono', 'Fira Code', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; + font-size: 0.88rem; + line-height: 1.6; + color: var(--color-text); + background: transparent; + border: none; +} + +/* Code block with header (language badge) */ + +.markdown-body .highlight { + position: relative; + background: color-mix(in srgb, var(--color-surface) 40%, transparent); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); + overflow: hidden; +} + +.markdown-body .highlight pre { + margin: 0; + border: none; + border-radius: 0; + box-shadow: none; +} + +/* Custom code block wrapper (if you want header with language/copy button) */ + +.mb-codeblock { + margin: 1.5rem 0; + border: 1px solid color-mix(in srgb, var(--color-border) 70%, transparent); + border-radius: 0.75rem; + overflow: hidden; + background: color-mix(in srgb, var(--color-surface) 40%, transparent); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); +} + +.mb-codeblock-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 0.75rem 1rem; + border-bottom: 1px solid color-mix(in srgb, var(--color-border) 60%, transparent); + background: color-mix(in srgb, var(--color-bg) 50%, transparent); +} + +.mb-codeblock-left { + display: flex; + align-items: center; + gap: 0.5rem; +} + +.mb-codeblock-label { + color: var(--color-text-muted); + font-size: 0.75rem; + font-weight: 500; + letter-spacing: 0.02em; +} + +.mb-codeblock-badge { + font-size: 0.7rem; + font-weight: 600; + padding: 0.2rem 0.5rem; + border-radius: 0.35rem; + background: color-mix(in srgb, var(--color-accent) 20%, transparent); + color: var(--color-accent); + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.mb-codeblock-actions { + display: flex; + gap: 0.25rem; +} + +.mb-codeblock-actions button { + background: transparent; + border: 1px solid color-mix(in srgb, var(--color-border) 70%, transparent); + color: var(--color-text-muted); + cursor: pointer; + font-size: 0.7rem; + padding: 0.3rem 0.6rem; + border-radius: 0.35rem; + transition: all 0.15s ease-out; + display: flex; + align-items: center; + gap: 0.35rem; +} + +.mb-codeblock-actions button:hover { + color: var(--color-accent); + background: color-mix(in srgb, var(--color-accent) 15%, transparent); + border-color: var(--color-accent); +} + +.mb-codeblock-content { + position: relative; +} + +.mb-codeblock-content pre { + margin: 0; + padding: 1.25rem 1.5rem; + overflow-x: auto; + border: none; + border-radius: 0; + background: transparent; + box-shadow: none; +} + +.mb-codeblock-content code { + font-family: 'JetBrains Mono', 'Fira Code', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; + font-size: 0.88rem; + line-height: 1.6; +} + +/* tables */ + +.markdown-body table { + width: 100%; + margin: 1.5rem 0; + border-collapse: collapse; + font-size: 0.9rem; + border-radius: 0.75rem; + overflow: hidden; + border: 1px solid color-mix(in srgb, var(--color-border) 70%, transparent); +} + +.markdown-body thead { + background: color-mix(in srgb, var(--color-surface) 60%, transparent); +} + +.markdown-body th { + padding: 0.75rem 1rem; + text-align: left; + font-weight: 600; + color: var(--color-text); + border-bottom: 1px solid color-mix(in srgb, var(--color-border) 70%, transparent); +} + +.markdown-body td { + padding: 0.75rem 1rem; + border-bottom: 1px solid color-mix(in srgb, var(--color-border) 60%, transparent); +} + +.markdown-body tbody tr:last-child td { + border-bottom: none; +} + +.markdown-body tbody tr:hover { + background: color-mix(in srgb, var(--color-surface) 30%, transparent); +} + +/* hr */ + +.markdown-body hr { + border: 0; + border-top: 1px solid color-mix(in srgb, var(--color-border) 70%, transparent); + margin: 2rem 0; +} + +/* Custom link with external indicator */ + +.markdown-body .md-link { + position: relative; +} + +.markdown-body a.md-link[target="_blank"]::after { + display: inline-block; + margin-left: 0.25rem; + font-size: 0.85em; + opacity: 0.6; + transition: all 0.15s ease-out; +} + +.markdown-body a.md-link[target="_blank"]:hover::after { + opacity: 1; + transform: translate(2px, -2px); +} + +/* Heading anchors with hover effect */ + +.markdown-body .md-heading-anchor { + position: relative; + color: inherit; + text-decoration: none; + border-bottom: none; + padding-bottom: 0; +} + +.markdown-body .md-heading-anchor::before { + content: '#'; + position: absolute; + left: -1.5rem; + opacity: 0; + color: var(--color-accent); + font-weight: 400; + transition: opacity 0.15s ease-out; +} + +.markdown-body h1:hover .md-heading-anchor::before, + .markdown-body h2:hover .md-heading-anchor::before, + .markdown-body h3:hover .md-heading-anchor::before, + .markdown-body h4:hover .md-heading-anchor::before, + .markdown-body h5:hover .md-heading-anchor::before, + .markdown-body h6:hover .md-heading-anchor::before { + opacity: 1; +} + +.markdown-body .md-heading-anchor:hover { + color: var(--color-accent); +} + +/* Table wrapper for horizontal scrolling */ + +.markdown-body .table-wrap { + margin: 1.75rem 0; + overflow-x: auto; + border-radius: 0.75rem; + border: 1px solid color-mix(in srgb, var(--color-border) 70%, transparent); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); +} + +.markdown-body .table-wrap table { + margin: 0; + border: none; + border-radius: 0; +} + +/* Custom scrollbar for table wrapper */ + +.markdown-body .table-wrap::-webkit-scrollbar { + height: 8px; +} + +.markdown-body .table-wrap::-webkit-scrollbar-track { + background: color-mix(in srgb, var(--color-bg) 30%, transparent); + border-radius: 0 0 0.75rem 0.75rem; +} + +.markdown-body .table-wrap::-webkit-scrollbar-thumb { + background: color-mix(in srgb, var(--color-accent) 50%, transparent); + border-radius: 4px; +} + +.markdown-body .table-wrap::-webkit-scrollbar-thumb:hover { + background: var(--color-accent); +} + +/* images */ + +.markdown-body img { + display: block; + max-width: 100%; + height: auto; + margin: 1.5rem auto; + border-radius: 0.75rem; + border: 1px solid color-mix(in srgb, var(--color-border) 60%, transparent); +} + +.markdown-body figure { + margin: 1.5rem 0; + text-align: center; +} + +.markdown-body figcaption { + margin-top: 0.5rem; + font-size: 0.85rem; + color: var(--color-text-muted); + font-style: italic; +} + +/* Custom image wrapper with lightbox */ + +.markdown-body .md-image { + margin: 2rem 0; + text-align: center; +} + +.markdown-body .md-image a { + display: inline-block; + position: relative; + overflow: hidden; + border-radius: 0.85rem; + border: 1px solid color-mix(in srgb, var(--color-border) 60%, transparent); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); + transition: all 0.25s ease-out; + border-bottom: none; + padding-bottom: 0; +} + +.markdown-body .md-image a::before { + content: ''; + position: absolute; + inset: 0; + background: linear-gradient( + 135deg, + color-mix(in srgb, var(--color-accent) 0%, transparent), + color-mix(in srgb, var(--color-accent) 15%, transparent) + ); + opacity: 0; + transition: opacity 0.25s ease-out; + pointer-events: none; + z-index: 1; +} + +.markdown-body .md-image a::after { + content: '🔍'; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%) scale(0.8); + font-size: 2.5rem; + opacity: 0; + transition: all 0.25s ease-out; + z-index: 2; + pointer-events: none; + filter: drop-shadow(0 2px 8px rgba(0, 0, 0, 0.5)); +} + +.markdown-body .md-image a:hover { + transform: translateY(-4px); + box-shadow: 0 12px 28px rgba(0, 0, 0, 0.25); + border-color: color-mix(in srgb, var(--color-accent) 50%, transparent); +} + +.markdown-body .md-image a:hover::before { + opacity: 1; +} + +.markdown-body .md-image a:hover::after { + opacity: 1; + transform: translate(-50%, -50%) scale(1); +} + +.markdown-body .md-image img { + margin: 0; + border: none; + border-radius: 0.75rem; + display: block; + transition: all 0.25s ease-out; +} + +.markdown-body .md-image a:hover img { + filter: brightness(0.85); +} + +.markdown-body .md-image figcaption { + display: none; +} + +/* SYNTAX HIGHLIGHTING - Dark theme colors */ + +/* These will be applied by Hugo's Chroma */ + +.markdown-body .chroma { + background: transparent; +} + +.markdown-body .chroma .err { + color: #f87171; +} + +.markdown-body .chroma .lntd { + padding: 0; +} + +.markdown-body .chroma .lntable { + border-spacing: 0; +} + +/* Chroma syntax colors for dark theme */ + +html[data-theme="dark"] .markdown-body .chroma .k { + color: #c084fc; +} + +/* keyword - purple */ + +html[data-theme="dark"] .markdown-body .chroma .kc { + color: #c084fc; +} + +html[data-theme="dark"] .markdown-body .chroma .kd { + color: #c084fc; +} + +html[data-theme="dark"] .markdown-body .chroma .kn { + color: #c084fc; +} + +html[data-theme="dark"] .markdown-body .chroma .kp { + color: #c084fc; +} + +html[data-theme="dark"] .markdown-body .chroma .kr { + color: #c084fc; +} + +html[data-theme="dark"] .markdown-body .chroma .kt { + color: #c084fc; +} + +html[data-theme="dark"] .markdown-body .chroma .n { + color: #e5e7eb; +} + +html[data-theme="dark"] .markdown-body .chroma .na { + color: #fbbf24; +} + +html[data-theme="dark"] .markdown-body .chroma .nb { + color: #60a5fa; +} + +html[data-theme="dark"] .markdown-body .chroma .nc { + color: #fbbf24; +} + +html[data-theme="dark"] .markdown-body .chroma .nf { + color: #60a5fa; +} + +html[data-theme="dark"] .markdown-body .chroma .nn { + color: #fbbf24; +} + +html[data-theme="dark"] .markdown-body .chroma .s { + color: #86efac; +} + +html[data-theme="dark"] .markdown-body .chroma .s1 { + color: #86efac; +} + +html[data-theme="dark"] .markdown-body .chroma .s2 { + color: #86efac; +} + +html[data-theme="dark"] .markdown-body .chroma .sb { + color: #86efac; +} + +html[data-theme="dark"] .markdown-body .chroma .sc { + color: #86efac; +} + +html[data-theme="dark"] .markdown-body .chroma .sd { + color: #86efac; +} + +html[data-theme="dark"] .markdown-body .chroma .se { + color: #fbbf24; +} + +html[data-theme="dark"] .markdown-body .chroma .si { + color: #fbbf24; +} + +html[data-theme="dark"] .markdown-body .chroma .sr { + color: #86efac; +} + +html[data-theme="dark"] .markdown-body .chroma .ss { + color: #86efac; +} + +html[data-theme="dark"] .markdown-body .chroma .m { + color: #fb923c; +} + +html[data-theme="dark"] .markdown-body .chroma .mb { + color: #fb923c; +} + +html[data-theme="dark"] .markdown-body .chroma .mf { + color: #fb923c; +} + +html[data-theme="dark"] .markdown-body .chroma .mh { + color: #fb923c; +} + +html[data-theme="dark"] .markdown-body .chroma .mi { + color: #fb923c; +} + +html[data-theme="dark"] .markdown-body .chroma .mo { + color: #fb923c; +} + +html[data-theme="dark"] .markdown-body .chroma .o { + color: #c084fc; +} + +html[data-theme="dark"] .markdown-body .chroma .ow { + color: #c084fc; +} + +html[data-theme="dark"] .markdown-body .chroma .c { + color: #6b7280; + font-style: italic; +} + +html[data-theme="dark"] .markdown-body .chroma .c1 { + color: #6b7280; + font-style: italic; +} + +html[data-theme="dark"] .markdown-body .chroma .cm { + color: #6b7280; + font-style: italic; +} + +html[data-theme="dark"] .markdown-body .chroma .p { + color: #9ca3af; +} + +html[data-theme="dark"] .markdown-body .chroma .g { + color: #ef4444; +} + +html[data-theme="dark"] .markdown-body .chroma .gd { + color: #ef4444; +} + +html[data-theme="dark"] .markdown-body .chroma .ge { + font-style: italic; +} + +html[data-theme="dark"] .markdown-body .chroma .gi { + color: #22c55e; +} + +html[data-theme="dark"] .markdown-body .chroma .gs { + font-weight: bold; +} + +/* SYNTAX HIGHLIGHTING - Light theme colors */ + +html[data-theme="light"] .markdown-body .chroma .k { + color: #7c3aed; +} + +/* keyword - purple */ + +html[data-theme="light"] .markdown-body .chroma .kc { + color: #7c3aed; +} + +html[data-theme="light"] .markdown-body .chroma .kd { + color: #7c3aed; +} + +html[data-theme="light"] .markdown-body .chroma .kn { + color: #7c3aed; +} + +html[data-theme="light"] .markdown-body .chroma .kp { + color: #7c3aed; +} + +html[data-theme="light"] .markdown-body .chroma .kr { + color: #7c3aed; +} + +html[data-theme="light"] .markdown-body .chroma .kt { + color: #7c3aed; +} + +html[data-theme="light"] .markdown-body .chroma .n { + color: #1f2937; +} + +html[data-theme="light"] .markdown-body .chroma .na { + color: #d97706; +} + +html[data-theme="light"] .markdown-body .chroma .nb { + color: #2563eb; +} + +html[data-theme="light"] .markdown-body .chroma .nc { + color: #d97706; +} + +html[data-theme="light"] .markdown-body .chroma .nf { + color: #2563eb; +} + +html[data-theme="light"] .markdown-body .chroma .nn { + color: #d97706; +} + +html[data-theme="light"] .markdown-body .chroma .s { + color: #16a34a; +} + +html[data-theme="light"] .markdown-body .chroma .s1 { + color: #16a34a; +} + +html[data-theme="light"] .markdown-body .chroma .s2 { + color: #16a34a; +} + +html[data-theme="light"] .markdown-body .chroma .sb { + color: #16a34a; +} + +html[data-theme="light"] .markdown-body .chroma .sc { + color: #16a34a; +} + +html[data-theme="light"] .markdown-body .chroma .sd { + color: #16a34a; +} + +html[data-theme="light"] .markdown-body .chroma .se { + color: #d97706; +} + +html[data-theme="light"] .markdown-body .chroma .si { + color: #d97706; +} + +html[data-theme="light"] .markdown-body .chroma .sr { + color: #16a34a; +} + +html[data-theme="light"] .markdown-body .chroma .ss { + color: #16a34a; +} + +html[data-theme="light"] .markdown-body .chroma .m { + color: #dc2626; +} + +html[data-theme="light"] .markdown-body .chroma .mb { + color: #dc2626; +} + +html[data-theme="light"] .markdown-body .chroma .mf { + color: #dc2626; +} + +html[data-theme="light"] .markdown-body .chroma .mh { + color: #dc2626; +} + +html[data-theme="light"] .markdown-body .chroma .mi { + color: #dc2626; +} + +html[data-theme="light"] .markdown-body .chroma .mo { + color: #dc2626; +} + +html[data-theme="light"] .markdown-body .chroma .o { + color: #7c3aed; +} + +html[data-theme="light"] .markdown-body .chroma .ow { + color: #7c3aed; +} + +html[data-theme="light"] .markdown-body .chroma .c { + color: #6b7280; + font-style: italic; +} + +html[data-theme="light"] .markdown-body .chroma .c1 { + color: #6b7280; + font-style: italic; +} + +html[data-theme="light"] .markdown-body .chroma .cm { + color: #6b7280; + font-style: italic; +} + +html[data-theme="light"] .markdown-body .chroma .p { + color: #4b5563; +} + +html[data-theme="light"] .markdown-body .chroma .g { + color: #dc2626; +} + +html[data-theme="light"] .markdown-body .chroma .gd { + color: #dc2626; +} + +html[data-theme="light"] .markdown-body .chroma .ge { + font-style: italic; +} + +html[data-theme="light"] .markdown-body .chroma .gi { + color: #16a34a; +} + +html[data-theme="light"] .markdown-body .chroma .gs { + font-weight: bold; +} + +.mb-codeblock { + margin: 1.75rem 0; + border: 1px solid color-mix(in srgb, var(--color-border) 65%, transparent); + border-radius: 0.85rem; + overflow: hidden; + background: color-mix(in srgb, var(--color-surface) 35%, transparent); + box-shadow: + 0 4px 6px rgba(0, 0, 0, 0.1), + 0 10px 20px rgba(0, 0, 0, 0.15); + transition: box-shadow 0.2s ease-out; +} + +.mb-codeblock:hover { + box-shadow: + 0 4px 6px rgba(0, 0, 0, 0.15), + 0 12px 24px rgba(0, 0, 0, 0.2); +} + +html[data-theme="light"] .mb-codeblock { + background: #f9fafb; + border-color: #e5e7eb; + box-shadow: + 0 2px 4px rgba(0, 0, 0, 0.05), + 0 4px 8px rgba(0, 0, 0, 0.08); +} + +html[data-theme="light"] .mb-codeblock:hover { + box-shadow: + 0 4px 8px rgba(0, 0, 0, 0.08), + 0 8px 16px rgba(0, 0, 0, 0.12); +} + +.mb-codeblock-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 0.7rem 1rem; + border-bottom: 1px solid color-mix(in srgb, var(--color-border) 55%, transparent); + background: color-mix(in srgb, var(--color-bg) 45%, transparent); + gap: 1rem; +} + +html[data-theme="light"] .mb-codeblock-header { + background: #f3f4f6; + border-bottom-color: #e5e7eb; +} + +.mb-codeblock-left { + display: flex; + align-items: center; + gap: 0.6rem; + min-width: 0; + flex: 1; +} + +.mb-codeblock-badge { + font-size: 0.68rem; + font-weight: 700; + padding: 0.25rem 0.55rem; + border-radius: 0.35rem; + background: color-mix(in srgb, var(--color-accent) 22%, transparent); + color: var(--color-accent); + text-transform: uppercase; + letter-spacing: 0.06em; + white-space: nowrap; + border: 1px solid color-mix(in srgb, var(--color-accent) 35%, transparent); +} + +.mb-codeblock-filename { + display: inline-flex; + align-items: center; + gap: 0.4rem; + color: var(--color-text); + font-size: 0.75rem; + font-weight: 500; + padding: 0.2rem 0.65rem; + border-radius: 0.35rem; + background: color-mix(in srgb, var(--color-bg) 35%, transparent); + border: 1px solid color-mix(in srgb, var(--color-border) 60%, transparent); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + max-width: 20rem; +} + +.mb-codeblock-actions { + display: flex; + gap: 0.4rem; + flex-shrink: 0; +} + +.mb-action-btn { + display: inline-flex; + align-items: center; + gap: 0.35rem; + background: transparent; + border: 1px solid color-mix(in srgb, var(--color-border) 70%, transparent); + color: var(--color-text-muted); + cursor: pointer; + font-size: 0.7rem; + font-weight: 500; + padding: 0.35rem 0.7rem; + border-radius: 0.4rem; + transition: all 0.15s ease-out; + font-family: inherit; + white-space: nowrap; +} + +.mb-action-btn:hover { + color: var(--color-accent); + background: color-mix(in srgb, var(--color-accent) 12%, transparent); + border-color: var(--color-accent); + transform: translateY(-1px); + box-shadow: 0 2px 8px rgba(168, 85, 247, 0.2); +} + +.mb-action-btn:active { + transform: translateY(0); +} + +.mb-action-btn i { + font-size: 0.7rem; +} + +.mb-btn-success { + color: #22c55e !important; + border-color: #22c55e !important; + background: color-mix(in srgb, #22c55e 15%, transparent) !important; +} + +.mb-codeblock-content { + position: relative; + background: color-mix(in srgb, var(--color-bg) 20%, transparent); + transition: max-height 0.3s ease-out; +} + +html[data-theme="light"] .mb-codeblock-content { + background: #ffffff; +} + +.mb-codeblock-content pre { + margin: 0; + padding: 1.25rem 1.5rem; + overflow-x: auto; + background: transparent; + border: none; +} + +.mb-codeblock-content pre code { + font-family: 'JetBrains Mono', 'Fira Code', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; + font-size: 0.88rem; + line-height: 1.65; + font-weight: 400; +} + +/* Custom scrollbar for code blocks */ + +.mb-codeblock-content pre::-webkit-scrollbar { + height: 8px; +} + +.mb-codeblock-content pre::-webkit-scrollbar-track { + background: color-mix(in srgb, var(--color-bg) 30%, transparent); + border-radius: 4px; +} + +.mb-codeblock-content pre::-webkit-scrollbar-thumb { + background: color-mix(in srgb, var(--color-border) 70%, transparent); + border-radius: 4px; +} + +.mb-codeblock-content pre::-webkit-scrollbar-thumb:hover { + background: var(--color-accent); +} + +/* Collapse overlay */ + +.mb-collapse-overlay { + display: none; + position: absolute; + inset: 0; + background: linear-gradient( + to bottom, + transparent 0%, + rgba(0, 0, 0, 0.25) 35%, + rgba(0, 0, 0, 0.88) 100% + ); + align-items: flex-end; + justify-content: center; + padding-bottom: 1.2rem; + cursor: pointer; + z-index: 10; + backdrop-filter: blur(1px); +} + +.mb-expand-trigger { + display: inline-flex; + align-items: center; + gap: 0.45rem; + padding: 0.5rem 1rem; + border-radius: 0.5rem; + border: 1px solid var(--color-accent); + background: color-mix(in srgb, var(--color-accent) 18%, transparent); + color: var(--color-accent); + font-size: 0.75rem; + font-weight: 650; + cursor: pointer; + transition: all 0.15s ease-out; + backdrop-filter: blur(10px); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); +} + +.mb-expand-trigger:hover { + background: color-mix(in srgb, var(--color-accent) 28%, transparent); + transform: translateY(-2px); + box-shadow: 0 6px 16px rgba(168, 85, 247, 0.35); + border-color: color-mix(in srgb, var(--color-accent) 90%, white); +} + +.mb-expand-trigger i { + font-size: 0.7rem; + animation: bounce-icon 1.8s ease-in-out infinite; +} + +@keyframes bounce-icon { + 0%, 100% { + transform: translateY(0); + } + + 50% { + transform: translateY(4px); + } +} + +.mb-codeblock[data-lang="javascript"] .mb-codeblock-badge, + .mb-codeblock[data-lang="js"] .mb-codeblock-badge { + background: color-mix(in srgb, #f7df1e 28%, transparent); + color: #f7df1e; + border-color: color-mix(in srgb, #f7df1e 40%, transparent); +} + +.mb-codeblock[data-lang="typescript"] .mb-codeblock-badge, + .mb-codeblock[data-lang="ts"] .mb-codeblock-badge { + background: color-mix(in srgb, #3178c6 28%, transparent); + color: #60a5fa; + border-color: color-mix(in srgb, #3178c6 40%, transparent); +} + +.mb-codeblock[data-lang="python"] .mb-codeblock-badge, + .mb-codeblock[data-lang="py"] .mb-codeblock-badge { + background: color-mix(in srgb, #3776ab 28%, transparent); + color: #60a5fa; + border-color: color-mix(in srgb, #3776ab 40%, transparent); +} + +.mb-codeblock[data-lang="go"] .mb-codeblock-badge { + background: color-mix(in srgb, #00add8 28%, transparent); + color: #22d3ee; + border-color: color-mix(in srgb, #00add8 40%, transparent); +} + +.mb-codeblock[data-lang="rust"] .mb-codeblock-badge, + .mb-codeblock[data-lang="rs"] .mb-codeblock-badge { + background: color-mix(in srgb, #ce422b 28%, transparent); + color: #fb923c; + border-color: color-mix(in srgb, #ce422b 40%, transparent); +} + +.mb-codeblock[data-lang="html"] .mb-codeblock-badge { + background: color-mix(in srgb, #e34c26 28%, transparent); + color: #f87171; + border-color: color-mix(in srgb, #e34c26 40%, transparent); +} + +.mb-codeblock[data-lang="css"] .mb-codeblock-badge { + background: color-mix(in srgb, #264de4 28%, transparent); + color: #60a5fa; + border-color: color-mix(in srgb, #264de4 40%, transparent); +} + +.mb-codeblock[data-lang="bash"] .mb-codeblock-badge, + .mb-codeblock[data-lang="sh"] .mb-codeblock-badge, + .mb-codeblock[data-lang="shell"] .mb-codeblock-badge { + background: color-mix(in srgb, #4eaa25 28%, transparent); + color: #86efac; + border-color: color-mix(in srgb, #4eaa25 40%, transparent); +} + +.mb-codeblock[data-lang="json"] .mb-codeblock-badge { + background: color-mix(in srgb, #888888 28%, transparent); + color: #d1d5db; + border-color: color-mix(in srgb, #888888 40%, transparent); +} + +.mb-codeblock[data-lang="yaml"] .mb-codeblock-badge, + .mb-codeblock[data-lang="yml"] .mb-codeblock-badge { + background: color-mix(in srgb, #cb171e 28%, transparent); + color: #fca5a5; + border-color: color-mix(in srgb, #cb171e 40%, transparent); +} + +.mb-codeblock[data-lang="java"] .mb-codeblock-badge { + background: color-mix(in srgb, #f89820 28%, transparent); + color: #fbbf24; + border-color: color-mix(in srgb, #f89820 40%, transparent); +} + +.mb-codeblock[data-lang="cpp"] .mb-codeblock-badge, + .mb-codeblock[data-lang="c++"] .mb-codeblock-badge { + background: color-mix(in srgb, #00599c 28%, transparent); + color: #60a5fa; + border-color: color-mix(in srgb, #00599c 40%, transparent); +} + +.mb-codeblock[data-lang="c"] .mb-codeblock-badge { + background: color-mix(in srgb, #555555 28%, transparent); + color: #9ca3af; + border-color: color-mix(in srgb, #555555 40%, transparent); +} + +.mb-codeblock[data-lang="ruby"] .mb-codeblock-badge, + .mb-codeblock[data-lang="rb"] .mb-codeblock-badge { + background: color-mix(in srgb, #cc342d 28%, transparent); + color: #f87171; + border-color: color-mix(in srgb, #cc342d 40%, transparent); +} + +.mb-codeblock[data-lang="php"] .mb-codeblock-badge { + background: color-mix(in srgb, #777bb4 28%, transparent); + color: #a78bfa; + border-color: color-mix(in srgb, #777bb4 40%, transparent); +} + +.mb-codeblock[data-lang="sql"] .mb-codeblock-badge { + background: color-mix(in srgb, #e38c00 28%, transparent); + color: #fbbf24; + border-color: color-mix(in srgb, #e38c00 40%, transparent); +} + +/* Responsive adjustments */ + +@media (max-width: 640px) { + .mb-codeblock-header { + flex-direction: column; + align-items: flex-start; + gap: 0.6rem; + } + + .mb-codeblock-actions { + width: 100%; + justify-content: flex-end; + } + + .mb-action-btn span { + display: none; + } + + .mb-action-btn { + padding: 0.4rem; + width: 2rem; + height: 2rem; + justify-content: center; + } + + .mb-codeblock-filename { + max-width: 100%; + } +} + +@keyframes tech-marquee { + 0% { + transform: translateX(0); + } + + 100% { + transform: translateX(-50%); + } +} + +@media (prefers-reduced-motion: reduce) { + .tech-strip-track { + animation: none; + } +} + +/* gallery */ + +.markdown-body .gallery-container { + margin: 2rem 0; + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: 1rem; +} + +@media (max-width: 640px) { + .markdown-body .gallery-container { + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 0.75rem; + } +} + +.markdown-body .gallery-container a { + display: block; + border: none !important; + padding: 0; + margin: 0; + border-radius: 0.5rem; + overflow: hidden; + transition: all 0.2s ease-out; + position: relative; + cursor: zoom-in; + aspect-ratio: 4/3; +} + +.markdown-body .gallery-container a:hover { + transform: translateY(-2px); + box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2); +} + +.markdown-body .gallery-container img { + display: block; + width: 100%; + height: 100%; + -o-object-fit: cover; + object-fit: cover; + margin: 0; + border: none; + border-radius: 0.5rem; + transition: all 0.2s ease-out; +} + +.markdown-body .gallery-container a:hover img { + filter: brightness(0.9); +} + +/* ==================== */ + +/* ABOUT PAGE */ + +/* ==================== */ + +/* ========================================================================== + TABLE OF CONTENTS + TOC sidebar, active link tracking, responsive behavior + ========================================================================== */ + +.article-layout { + display: grid; + grid-template-columns: 1fr; + gap: 2rem; + position: relative; + min-width: 0; +} + +@media (min-width: 1024px) { + .article-layout { + grid-template-columns: 260px 1fr; + gap: 3rem; + } + + .article-toc { + order: 1; + } + + .article-main { + order: 2; + } +} + +.article-main { + min-width: 0; + overflow-x: hidden; +} + +.article-toc { + width: 100%; +} + +.toc-wrapper { + position: sticky; + top: 2rem; + background: color-mix(in srgb, var(--color-surface) 50%, transparent); + border: 1px solid var(--color-border); + border-radius: 0.85rem; + padding: 1rem; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); + backdrop-filter: blur(10px); + transition: all 0.2s ease-out; + max-height: calc(100vh - 24rem); + overflow: hidden; + display: flex; + flex-direction: column; +} + +.toc-wrapper:hover { + box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2); + border-color: color-mix(in srgb, var(--color-accent) 40%, transparent); +} + +.toc-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 0.75rem; + padding-bottom: 0.75rem; + border-bottom: 1px solid color-mix(in srgb, var(--color-border) 60%, transparent); +} + +.toc-title { + display: flex; + align-items: center; + gap: 0.5rem; + font-size: 0.85rem; + font-weight: 650; + color: var(--color-text); + margin: 0; +} + +.toc-title i { + color: var(--color-accent); + font-size: 0.75rem; +} + +.toc-toggle { + display: flex; + background: transparent; + border: 1px solid var(--color-border); + color: var(--color-text-muted); + width: 1.75rem; + height: 1.75rem; + border-radius: 0.4rem; + cursor: pointer; + transition: all 0.15s ease-out; + align-items: center; + justify-content: center; +} + +.toc-toggle:hover { + color: var(--color-accent); + border-color: var(--color-accent); + background: color-mix(in srgb, var(--color-accent) 10%, transparent); +} + +.toc-toggle i { + font-size: 0.7rem; + transition: transform 0.2s ease-out; +} + +.toc-wrapper.collapsed .toc-toggle i { + transform: rotate(-90deg); +} + +.toc-wrapper.collapsed .toc-nav { + max-height: 0; + opacity: 0; + overflow: hidden; +} + +@media (max-width: 1023px) { + .toc-wrapper { + position: relative; + top: 0; + } +} + +.toc-nav { + font-size: 0.8rem; + line-height: 1.6; + transition: all 0.3s ease-out; + overflow-y: auto; + overflow-x: hidden; + flex: 1; + min-height: 0; +} + +/* Custom scrollbar for TOC */ + +.toc-nav::-webkit-scrollbar { + width: 6px; +} + +.toc-nav::-webkit-scrollbar-track { + background: color-mix(in srgb, var(--color-bg) 30%, transparent); + border-radius: 3px; +} + +.toc-nav::-webkit-scrollbar-thumb { + background: color-mix(in srgb, var(--color-border) 70%, transparent); + border-radius: 3px; +} + +.toc-nav::-webkit-scrollbar-thumb:hover { + background: var(--color-accent); +} + +.toc-nav > ul { + list-style: none; + padding-left: 0; + margin: 0; +} + +.toc-nav ul { + list-style: none; + margin: 0; +} + +.toc-nav ul ul { + padding-left: 1rem; + margin-top: 0.25rem; + border-left: 1px solid color-mix(in srgb, var(--color-border) 50%, transparent); +} + +.toc-nav li { + margin: 0.35rem 0; + padding-left: 0; +} + +.toc-nav li:hover { + background: none; +} + +.toc-nav a { + display: block; + padding: 0.35rem 0.5rem; + color: var(--color-text-muted); + text-decoration: none; + border-left: 2px solid transparent; + border-radius: 0.35rem; + transition: all 0.15s ease-out; + border-bottom: none; +} + +.toc-nav a:hover { + color: var(--color-text); + background: color-mix(in srgb, var(--color-surface) 40%, transparent); + border-left-color: var(--color-accent); + transform: translateX(3px); +} + +.toc-nav a.active { + color: var(--color-accent); + background: color-mix(in srgb, var(--color-accent) 12%, transparent); + border-left-color: var(--color-accent); + font-weight: 600; +} + +/* Mobile: TOC appears at top */ + +@media (max-width: 1023px) { + .article-toc { + order: -1; + margin-bottom: 1.5rem; + } +} + +/* Pages */ + +/* ========================================================================== + ABOUT PAGE STYLES + Standard about page with timeline + ========================================================================== */ + +.about-page { + max-width: 900px; + margin-inline: auto; +} + +.about-hero { + text-align: center; + padding: 3rem 0 4rem; + position: relative; +} + +.about-hero::after { + content: ''; + position: absolute; + bottom: 0; + left: 50%; + transform: translateX(-50%); + width: 60px; + height: 3px; + background: linear-gradient( + 90deg, + transparent, + var(--color-accent), + transparent + ); + border-radius: 999px; +} + +.about-hero-content { + display: flex; + flex-direction: column; + align-items: center; + gap: 1.5rem; +} + +.about-avatar, + .about-avatar-placeholder { + width: 140px; + height: 140px; + border-radius: 50%; + overflow: hidden; + border: 3px solid var(--color-accent); + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2), + 0 0 0 8px color-mix(in srgb, var(--color-accent) 15%, transparent); + transition: all 0.3s ease-out; +} + +.about-avatar:hover, + .about-avatar-placeholder:hover { + transform: translateY(-4px) scale(1.02); + box-shadow: 0 12px 32px rgba(0, 0, 0, 0.3), + 0 0 0 12px color-mix(in srgb, var(--color-accent) 20%, transparent); +} + +.about-avatar img { + width: 100%; + height: 100%; + -o-object-fit: cover; + object-fit: cover; +} + +.about-avatar-placeholder { + display: flex; + align-items: center; + justify-content: center; + background: linear-gradient( + 135deg, + color-mix(in srgb, var(--color-accent) 20%, transparent), + color-mix(in srgb, var(--color-accent) 10%, transparent) + ); + backdrop-filter: blur(10px); +} + +.about-avatar-placeholder i { + font-size: 4rem; + color: var(--color-accent); +} + +.about-title { + font-size: 2.5rem; + font-weight: 700; + letter-spacing: -0.02em; + color: var(--color-text); + margin: 0; + line-height: 1.2; +} + +.about-subtitle { + font-size: 1.1rem; + color: var(--color-text-muted); + max-width: 600px; + line-height: 1.6; + margin: 0; +} + +.about-content { + margin-bottom: 4rem; +} + +.about-content .card { + background: color-mix(in srgb, var(--color-surface) 60%, transparent); + border: 1px solid color-mix(in srgb, var(--color-border) 70%, transparent); + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12); + padding: 2rem; +} + +.about-content .markdown-body h3 { + display: flex; + align-items: center; + gap: 0.75rem; + margin-top: 2.5rem; + padding-bottom: 0.5rem; + border-bottom: 2px solid color-mix(in srgb, var(--color-accent) 30%, transparent); +} + +.about-content .markdown-body h3::before { + content: ''; + display: inline-block; + width: 4px; + height: 1.5rem; + background: var(--color-accent); + border-radius: 999px; +} + +/* Timeline */ + +.timeline { + position: relative; + padding: 2rem 0 1rem 0; + margin-top: 2rem; +} + +.timeline::before { + content: ''; + position: absolute; + left: 20px; + top: 0; + bottom: 0; + width: 2px; + background: linear-gradient( + to bottom, + transparent, + var(--color-accent) 10%, + var(--color-accent) 90%, + transparent + ); +} + +.timeline-item { + position: relative; + padding-left: 60px; + margin-bottom: 3rem; +} + +.timeline-item:last-child { + margin-bottom: 0; +} + +.timeline-marker { + position: absolute; + left: 0; + top: 8px; + width: 42px; + height: 42px; + display: flex; + align-items: center; + justify-content: center; + z-index: 2; +} + +.timeline-marker::before { + content: ''; + position: absolute; + width: 16px; + height: 16px; + background: var(--color-accent); + border-radius: 50%; + border: 3px solid var(--color-bg); + box-shadow: + 0 0 0 4px color-mix(in srgb, var(--color-accent) 20%, transparent), + 0 4px 12px rgba(0, 0, 0, 0.3); + transition: all 0.3s ease-out; +} + +.timeline-item:hover .timeline-marker::before { + transform: scale(1.3); + box-shadow: + 0 0 0 6px color-mix(in srgb, var(--color-accent) 30%, transparent), + 0 6px 16px rgba(0, 0, 0, 0.4); +} + +.timeline-content { + background: color-mix(in srgb, var(--color-surface) 30%, transparent); + border: 1px solid color-mix(in srgb, var(--color-border) 50%, transparent); + border-radius: 0.85rem; + padding: 1.5rem; + transition: all 0.3s ease-out; + position: relative; + overflow: hidden; +} + +.timeline-content::before { + content: ''; + position: absolute; + left: 0; + top: 0; + bottom: 0; + width: 4px; + background: var(--color-accent); + opacity: 0; + transition: opacity 0.3s ease-out; +} + +.timeline-item:hover .timeline-content { + background: color-mix(in srgb, var(--color-surface) 50%, transparent); + border-color: color-mix(in srgb, var(--color-accent) 40%, transparent); + transform: translateX(4px); + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2); +} + +.timeline-item:hover .timeline-content::before { + opacity: 1; +} + +.timeline-content > p:first-child { + margin-top: 0; +} + +.timeline-content > p:last-child { + margin-bottom: 0; +} + +.timeline-content strong { + font-size: 1.1rem; + color: var(--color-text); + display: block; + margin-bottom: 0.25rem; +} + +.timeline-content em { + font-size: 0.85rem; + color: var(--color-text-muted); + font-style: normal; + display: block; + margin-bottom: 0.75rem; +} + +.timeline-content a { + color: var(--color-accent); +} + +/* Remove hr from timeline */ + +.timeline hr { + display: none; +} + +.about-social { + text-align: center; + padding: 3rem 2rem; + background: color-mix(in srgb, var(--color-surface) 40%, transparent); + border: 1px solid color-mix(in srgb, var(--color-border) 60%, transparent); + border-radius: 1.25rem; + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1); +} + +.about-social-title { + font-size: 1.25rem; + font-weight: 650; + color: var(--color-text); + margin-bottom: 1.5rem; +} + +.about-social-links { + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 1rem; +} + +.about-social-link { + display: inline-flex; + align-items: center; + gap: 0.5rem; + padding: 0.75rem 1.5rem; + border-radius: 999px; + background: color-mix(in srgb, var(--color-bg) 60%, transparent); + border: 1px solid color-mix(in srgb, var(--color-border) 70%, transparent); + color: var(--color-text-muted); + text-decoration: none; + font-size: 0.9rem; + font-weight: 500; + transition: all 0.2s ease-out; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); +} + +.about-social-link:hover { + transform: translateY(-2px); + background: color-mix(in srgb, var(--color-surface) 80%, transparent); + border-color: var(--color-accent); + color: var(--color-accent); + box-shadow: 0 6px 20px rgba(0, 0, 0, 0.15); +} + +.about-social-link i { + font-size: 1.1rem; +} + +/* Responsive about page */ + +@media (max-width: 640px) { + .about-hero { + padding: 2rem 0 3rem; + } + + .about-avatar, + .about-avatar-placeholder { + width: 110px; + height: 110px; + } + + .about-avatar-placeholder i { + font-size: 3rem; + } + + .about-title { + font-size: 2rem; + } + + .about-subtitle { + font-size: 1rem; + } + + .about-social { + padding: 2rem 1.25rem; + } + + .about-social-links { + flex-direction: column; + align-items: stretch; + } + + .about-social-link { + justify-content: center; + } + + /* Timeline responsive */ + + .timeline::before { + left: 12px; + } + + .timeline-item { + padding-left: 40px; + } + + .timeline-marker { + left: -5px; + width: 34px; + height: 34px; + } + + .timeline-marker::before { + width: 12px; + height: 12px; + } + + .timeline-content { + padding: 1rem; + } + + .timeline-content strong { + font-size: 1rem; + } + + .timeline-content em { + font-size: 0.8rem; + } +} + +/* ========================================================================== + ABOUT ALTERNATIVE PAGE STYLES + Alternative about page with sidebar profile card + ========================================================================== */ + +.page-int, .about-alt-page { + max-width: 1200px; + margin-inline: auto; +} + +.about-alt-layout { + display: grid; + grid-template-columns: 1fr; + gap: 2rem; + padding: 2rem 0; +} + +@media (min-width: 1024px) { + .about-alt-layout { + grid-template-columns: 320px 1fr; + gap: 3rem; + } +} + +/* Profile Card - Left Sidebar */ + +.about-alt-sidebar { + position: relative; +} + +.about-alt-profile-card { + background: linear-gradient( + 135deg, + color-mix(in srgb, var(--color-surface) 70%, transparent), + color-mix(in srgb, var(--color-surface) 50%, transparent) + ); + border: 1px solid color-mix(in srgb, var(--color-border) 60%, transparent); + border-radius: 1.5rem; + padding: 2rem; + text-align: center; + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2); + position: sticky; + top: 2rem; +} + +.about-alt-avatar, + .about-alt-avatar-placeholder { + width: 120px; + height: 120px; + border-radius: 50%; + margin: 0 auto 1.5rem; + overflow: hidden; + border: 4px solid var(--color-accent); + box-shadow: 0 0 0 8px color-mix(in srgb, var(--color-accent) 15%, transparent); + transition: all 0.3s ease-out; +} + +.about-alt-avatar:hover, + .about-alt-avatar-placeholder:hover { + transform: scale(1.05); + box-shadow: 0 0 0 12px color-mix(in srgb, var(--color-accent) 20%, transparent); +} + +.about-alt-avatar img { + width: 100%; + height: 100%; + -o-object-fit: cover; + object-fit: cover; +} + +.about-alt-avatar-placeholder { + display: flex; + align-items: center; + justify-content: center; + background: linear-gradient( + 135deg, + color-mix(in srgb, var(--color-accent) 30%, transparent), + color-mix(in srgb, var(--color-accent) 15%, transparent) + ); +} + +.about-alt-avatar-placeholder i { + font-size: 3rem; + color: var(--color-accent); +} + +.about-alt-name { + font-size: 1.75rem; + font-weight: 700; + color: var(--color-text); + margin-bottom: 0.5rem; +} + +.about-alt-role { + font-size: 0.9rem; + color: var(--color-text-muted); + line-height: 1.5; + margin-bottom: 1rem; +} + +.about-alt-meta { + display: flex; + align-items: center; + justify-content: center; + gap: 0.5rem; + font-size: 0.85rem; + color: var(--color-text-muted); + margin-bottom: 1.5rem; +} + +.about-alt-meta i { + color: var(--color-accent); +} + +/* Stats Grid */ + +.about-alt-stats { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 1rem; + margin: 2rem 0; + padding: 1.5rem 0; + border-top: 1px solid color-mix(in srgb, var(--color-border) 50%, transparent); + border-bottom: 1px solid color-mix(in srgb, var(--color-border) 50%, transparent); +} + +.about-alt-stat { + text-align: center; +} + +.about-alt-stat-value { + font-size: 1.75rem; + font-weight: 700; + color: var(--color-accent); + margin-bottom: 0.25rem; +} + +.about-alt-stat-label { + font-size: 0.7rem; + color: var(--color-text-muted); + text-transform: uppercase; + letter-spacing: 0.05em; +} + +/* Social Icons */ + +.about-alt-social { + display: flex; + justify-content: center; + gap: 0.75rem; + margin-top: 1.5rem; +} + +.about-alt-social-icon { + display: flex; + align-items: center; + justify-content: center; + width: 2.5rem; + height: 2.5rem; + border-radius: 50%; + background: color-mix(in srgb, var(--color-bg) 50%, transparent); + border: 1px solid color-mix(in srgb, var(--color-border) 60%, transparent); + color: var(--color-text-muted); + transition: all 0.2s ease-out; +} + +.about-alt-social-icon:hover { + background: var(--color-accent); + border-color: var(--color-accent); + color: white; + transform: translateY(-3px); + box-shadow: 0 6px 16px rgba(0, 0, 0, 0.3); +} + +.about-alt-social-icon i { + font-size: 1rem; +} + +/* Content Area */ + +.about-alt-content { + display: flex; + flex-direction: column; + gap: 2rem; +} + +.about-alt-section { + background: color-mix(in srgb, var(--color-surface) 40%, transparent); + border: 1px solid color-mix(in srgb, var(--color-border) 60%, transparent); + border-radius: 1.25rem; + padding: 2rem; + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15); +} + +.about-alt-section-title { + display: flex; + align-items: center; + gap: 0.75rem; + font-size: 1.5rem; + font-weight: 700; + color: var(--color-text); + margin-bottom: 1.5rem; + padding-bottom: 1rem; + border-bottom: 2px solid color-mix(in srgb, var(--color-accent) 20%, transparent); +} + +.about-alt-section-title i { + color: var(--color-accent); + font-size: 1.25rem; +} + +/* Experience Cards Grid */ + +.about-alt-experience-grid { + display: grid; + gap: 1.5rem; +} + +.about-alt-experience-card { + background: color-mix(in srgb, var(--color-bg) 40%, transparent); + border: 1px solid color-mix(in srgb, var(--color-border) 50%, transparent); + border-left: 4px solid var(--color-accent); + border-radius: 0.85rem; + padding: 1.5rem; + transition: all 0.3s ease-out; + position: relative; + overflow: hidden; +} + +.about-alt-experience-card::before { + content: ''; + position: absolute; + top: 0; + right: 0; + width: 100px; + height: 100px; + background: radial-gradient( + circle at center, + color-mix(in srgb, var(--color-accent) 10%, transparent), + transparent 70% + ); + opacity: 0; + transition: opacity 0.3s ease-out; +} + +.about-alt-experience-card:hover { + border-left-width: 6px; + transform: translateX(6px); + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.25); +} + +.about-alt-experience-card:hover::before { + opacity: 1; +} + +.about-alt-experience-card p:first-child { + margin-top: 0; +} + +.about-alt-experience-card p:last-child { + margin-bottom: 0; +} + +.about-alt-experience-card strong { + font-size: 1.1rem; + color: var(--color-text); + display: block; + margin-bottom: 0.25rem; +} + +.about-alt-experience-card em { + font-size: 0.85rem; + color: var(--color-text-muted); + font-style: normal; + display: block; + margin-bottom: 0.75rem; +} + +/* Skills Badge Cloud */ + +.about-alt-skills { + display: flex; + flex-wrap: wrap; + gap: 0.75rem; +} + +.about-alt-skill { + display: inline-flex; + align-items: center; + gap: 0.5rem; + padding: 0.5rem 1rem; + background: color-mix(in srgb, var(--color-bg) 50%, transparent); + border: 1px solid color-mix(in srgb, var(--color-border) 60%, transparent); + border-radius: 999px; + font-size: 0.85rem; + font-weight: 500; + color: var(--color-text); + transition: all 0.2s ease-out; + cursor: default; +} + +.about-alt-skill i { + font-size: 1.1em; + opacity: 0.9; +} + +.about-alt-skill:hover { + background: var(--color-accent); + border-color: var(--color-accent); + color: white; + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); +} + +.about-alt-skill:hover i { + opacity: 1; +} + +/* Responsive */ + +@media (max-width: 1023px) { + .about-alt-profile-card { + position: relative; + top: 0; + } + + .about-alt-layout { + grid-template-columns: 1fr; + } +} + +@media (max-width: 640px) { + .about-alt-profile-card { + padding: 1.5rem; + } + + .about-alt-avatar, + .about-alt-avatar-placeholder { + width: 100px; + height: 100px; + } + + .about-alt-name { + font-size: 1.5rem; + } + + .about-alt-stats { + gap: 0.75rem; + } + + .about-alt-stat-value { + font-size: 1.5rem; + } + + .about-alt-section { + padding: 1.5rem; + } + + .about-alt-section-title { + font-size: 1.25rem; + } + + .about-alt-experience-card { + padding: 1.25rem; + } +} + +/* ==================== */ + +/* TABLE OF CONTENTS */ + +/* ==================== */ + +/* Responsive Styles */ + +/* ========================================================================== + RESPONSIVE STYLES + Global responsive breakpoints and mobile optimizations + ========================================================================== */ + +/* Mobile - Small screens (up to 640px) */ + +@media (max-width: 640px) { + .card-pad { + padding: 1.25rem; + } + + .layout-page { + padding-inline: 1rem; + padding-block: 1.5rem; + } + + .layout-page-tight { + padding-inline: 1rem; + padding-block: 1rem; + } + + /* Markdown body */ + + .markdown-body { + font-size: 0.9rem; + padding: 0; + } + + /* Headings - scale down for mobile */ + + .markdown-body h1 { + font-size: 1.75rem; + } + + .markdown-body h2 { + font-size: 1.35rem; + margin-top: 2rem; + } + + .markdown-body h3 { + font-size: 1.15rem; + margin-top: 1.5rem; + } + + .markdown-body h4 { + font-size: 1rem; + } + + .markdown-body h5, + .markdown-body h6 { + font-size: 0.9rem; + } + + /* Heading anchors - adjust for smaller screens */ + + .markdown-body .md-heading-anchor::before { + left: -1rem; + font-size: 0.85em; + } + + /* Blockquotes */ + + .markdown-body blockquote, + .markdown-body .md-blockquote { + margin: 1.25rem 0; + padding: 1rem; + border-left-width: 3px; + } + + .markdown-body blockquote::before, + .markdown-body .md-blockquote::before { + font-size: 2rem; + left: 0.5rem; + top: 0.25rem; + } + + /* Alerts */ + + .markdown-body .md-alert { + padding: 0.85rem 1rem 0.85rem 2.5rem; + } + + .markdown-body .md-alert::before { + left: 0.75rem; + top: 0.95rem; + font-size: 0.95rem; + } + + /* Lists */ + + .markdown-body ul, + .markdown-body ol { + padding-left: 1.25rem; + } + + /* Code blocks */ + + .markdown-body pre { + margin: 1rem -1rem; + border-radius: 0.5rem; + } + + .markdown-body pre code { + font-size: 0.8rem; + padding: 1rem; + } + + .mb-codeblock { + margin: 1.25rem -1rem; + border-radius: 0.5rem; + } + + .mb-codeblock-content pre { + padding: 1rem; + } + + .mb-codeblock-content pre code { + font-size: 0.8rem; + } + + /* Inline code */ + + .markdown-body :not(pre) > code { + font-size: 0.85em; + padding: 0.1rem 0.35rem; + } + + /* Tables - full bleed on mobile */ + + .markdown-body table { + font-size: 0.85rem; + } + + .markdown-body th, + .markdown-body td { + padding: 0.5rem 0.65rem; + } + + .markdown-body .table-wrap { + margin: 1.25rem -1rem; + border-radius: 0.5rem; + } + + /* Images */ + + .markdown-body img, + .markdown-body .md-image { + margin: 1.25rem 0; + } + + .markdown-body .md-image a { + border-radius: 0.5rem; + } + + .markdown-body .md-image a::after { + font-size: 2rem; + } + + .markdown-body .md-image figcaption { + font-size: 0.82rem; + padding: 0.4rem 0.75rem; + } + + /* Page heading */ + + .heading-page { + font-size: 1.75rem !important; + line-height: 1.2; + } + + /* Article header */ + + .article-main header { + padding: 0; + } + + /* TOC on mobile */ + + .toc-wrapper { + border-radius: 0.75rem; + padding: 0.85rem; + } + + .toc-nav { + font-size: 0.85rem; + } +} + +/* Tablet - Medium screens (641px to 1023px) */ + +@media (min-width: 641px) and (max-width: 1023px) { + .article-layout { + display: flex; + flex-wrap: wrap; + } + + .article-main { + max-width: -webkit-fill-available; + } + + .layout-page { + padding-inline: 1.5rem; + padding-block: 2rem; + } + + .markdown-body { + font-size: 0.93rem; + } + + .markdown-body h1 { + font-size: 1.85rem; + } + + .markdown-body h2 { + font-size: 1.4rem; + } + + .markdown-body h3 { + font-size: 1.2rem; + } +} + +/* Larger screens - Adjust max-widths */ + +@media (min-width: 1536px) { + .article-layout { + padding-inline: 2rem; + } +} + +/* Ultra-wide screens */ + +@media (min-width: 1920px) { + .markdown-body { + font-size: 1rem; + } +} + +/* Touch device improvements */ + +@media (hover: none) and (pointer: coarse) { + /* Increase tap targets for touch */ + + .toc-nav a { + padding: 0.5rem 0.65rem; + margin: 0.15rem 0; + } + + .toc-toggle { + width: 2.5rem; + height: 2.5rem; + } + + .mb-action-btn { + padding: 0.5rem 0.85rem; + font-size: 0.75rem; + } + + /* Remove hover effects on touch devices */ + + .markdown-body li:hover { + background: none; + } + + .card-hover:hover { + transform: none; + box-shadow: none; + } +} + +/* Landscape mobile */ + +@media (max-height: 500px) and (orientation: landscape) { + .toc-wrapper { + position: relative; + top: 0; + } + + .layout-page { + padding-block: 1rem; + } +} + +.last\:border-0:last-child { + border-width: 0px; +} + +.hover\:text-accent:hover { + color: var(--color-accent); +} + +.group:hover .group-hover\:text-accent { + color: var(--color-accent); +} + +@media (min-width: 640px) { + .sm\:h-28 { + height: 7rem; + } + + .sm\:w-28 { + width: 7rem; + } + + .sm\:px-6 { + padding-left: 1.5rem; + padding-right: 1.5rem; + } + + .sm\:text-3xl { + font-size: 1.875rem; + line-height: 2.25rem; + } + + .sm\:text-4xl { + font-size: 2.25rem; + line-height: 2.5rem; + } +} + +@media (min-width: 768px) { + .md\:order-none { + order: 0; + } + + .md\:flex { + display: flex; + } + + .md\:hidden { + display: none; + } + + .md\:grid-cols-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .md\:grid-cols-\[minmax\(0\2c 2fr\)_minmax\(0\2c 1\.2fr\)\] { + grid-template-columns: minmax(0,2fr) minmax(0,1.2fr); + } + + .md\:justify-end { + justify-content: flex-end; + } +} diff --git a/themes/minimal-black/static/icons/apple-touch-icon.png b/themes/minimal-black/static/icons/apple-touch-icon.png new file mode 100644 index 0000000..0cb3ba6 Binary files /dev/null and b/themes/minimal-black/static/icons/apple-touch-icon.png differ diff --git a/themes/minimal-black/static/icons/favicon-96x96.png b/themes/minimal-black/static/icons/favicon-96x96.png new file mode 100644 index 0000000..7ad42ae Binary files /dev/null and b/themes/minimal-black/static/icons/favicon-96x96.png differ diff --git a/themes/minimal-black/static/icons/favicon.ico b/themes/minimal-black/static/icons/favicon.ico new file mode 100644 index 0000000..c04a684 Binary files /dev/null and b/themes/minimal-black/static/icons/favicon.ico differ diff --git a/themes/minimal-black/static/icons/favicon.svg b/themes/minimal-black/static/icons/favicon.svg new file mode 100644 index 0000000..0774ac6 --- /dev/null +++ b/themes/minimal-black/static/icons/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/themes/minimal-black/static/icons/site.webmanifest b/themes/minimal-black/static/icons/site.webmanifest new file mode 100644 index 0000000..0a0d4c8 --- /dev/null +++ b/themes/minimal-black/static/icons/site.webmanifest @@ -0,0 +1,21 @@ +{ + "name": "Minimal Black", + "short_name": "M-BLK", + "icons": [ + { + "src": "/web-app-manifest-192x192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "/web-app-manifest-512x512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ], + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone" +} \ No newline at end of file diff --git a/themes/minimal-black/static/icons/web-app-manifest-192x192.png b/themes/minimal-black/static/icons/web-app-manifest-192x192.png new file mode 100644 index 0000000..72b6fc1 Binary files /dev/null and b/themes/minimal-black/static/icons/web-app-manifest-192x192.png differ diff --git a/themes/minimal-black/static/icons/web-app-manifest-512x512.png b/themes/minimal-black/static/icons/web-app-manifest-512x512.png new file mode 100644 index 0000000..350104d Binary files /dev/null and b/themes/minimal-black/static/icons/web-app-manifest-512x512.png differ diff --git a/themes/minimal-black/static/images/logo.png b/themes/minimal-black/static/images/logo.png new file mode 100644 index 0000000..9551900 Binary files /dev/null and b/themes/minimal-black/static/images/logo.png differ diff --git a/themes/minimal-black/static/js/gallery.js b/themes/minimal-black/static/js/gallery.js new file mode 100644 index 0000000..290b493 --- /dev/null +++ b/themes/minimal-black/static/js/gallery.js @@ -0,0 +1,16 @@ +document.addEventListener("DOMContentLoaded", function () { + if (typeof window.$ === "undefined" && typeof window.JustifiedGallery === "undefined") { + // using vanilla JustifiedGallery from CDN, globally exposed + } + + var roots = document.querySelectorAll("[data-jg]"); + if (!roots.length || typeof window.JustifiedGallery === "undefined") return; + + roots.forEach(function (el) { + new window.JustifiedGallery(el, { + rowHeight: 260, + lastRow: "center", + margin: 16, + }); + }); +}); diff --git a/themes/minimal-black/static/js/lightbox.js b/themes/minimal-black/static/js/lightbox.js new file mode 100644 index 0000000..72c9c7a --- /dev/null +++ b/themes/minimal-black/static/js/lightbox.js @@ -0,0 +1,11 @@ +document.addEventListener("DOMContentLoaded", function () { + if (typeof GLightbox === "undefined") return; + + GLightbox({ + selector: ".glightbox", + loop: true, + touchNavigation: true, + zoomable: true, + draggable: true, + }); +}); diff --git a/themes/minimal-black/static/js/main.js b/themes/minimal-black/static/js/main.js new file mode 100644 index 0000000..8d6951d --- /dev/null +++ b/themes/minimal-black/static/js/main.js @@ -0,0 +1,137 @@ +(function () { + var STORAGE_KEY = "theme"; + /* ---------- Theme ---------- */ + + function getStoredTheme() { + try { + return localStorage.getItem(STORAGE_KEY); + } catch (e) { + return null; + } + } + + function storeTheme(theme) { + try { + localStorage.setItem(STORAGE_KEY, theme); + } catch (e) {} + } + + function getSystemTheme() { + return window.matchMedia && + window.matchMedia("(prefers-color-scheme: dark)").matches + ? "dark" + : "light"; + } + + function applyTheme(theme) { + document.documentElement.setAttribute("data-theme", theme); + + var isDark = theme === "dark"; + var lightIcons = document.querySelectorAll("[data-theme-icon-light]"); + var darkIcons = document.querySelectorAll("[data-theme-icon-dark]"); + + lightIcons.forEach(function (el) { + el.style.display = isDark ? "" : "none"; + }); + darkIcons.forEach(function (el) { + el.style.display = isDark ? "none" : ""; + }); + } + + function initThemeFromDOM() { + var attr = document.documentElement.getAttribute("data-theme"); + if (attr === "dark" || attr === "light") { + applyTheme(attr); + return; + } + var stored = getStoredTheme(); + applyTheme(stored || getSystemTheme()); + } + + function toggleTheme() { + var current = + document.documentElement.getAttribute("data-theme") || "light"; + var next = current === "dark" ? "light" : "dark"; + applyTheme(next); + storeTheme(next); + } + + function initThemeToggle() { + var btns = document.querySelectorAll("[data-theme-toggle]"); + if (!btns.length) return; + btns.forEach(function (btn) { + btn.addEventListener("click", toggleTheme); + }); + } + + /* ---------- Mobile nav ---------- */ + + function initMobileNav() { + var toggle = document.querySelector("[data-mobile-nav-toggle]"); + var nav = document.querySelector("[data-mobile-nav]"); + if (!toggle || !nav) return; + + var open = false; + function setOpen(next) { + open = next; + nav.style.display = open ? "block" : "none"; + } + + toggle.addEventListener("click", function () { + setOpen(!open); + }); + + nav.addEventListener("click", function (e) { + if (e.target.tagName === "A") setOpen(false); + }); + } + + /* ---------- Dock ---------- */ + + function initDock() { + var dock = document.querySelector("[data-dock]"); + if (!dock) return; + + var toggle = dock.querySelector("[data-dock-toggle]"); + var backTop = dock.querySelector('[data-dock-action="top"]'); + var backBtn = dock.querySelector('[data-dock-action="back"]'); + var open = false; + + function setOpen(next) { + open = next; + dock.classList.toggle("dock--open", open); + } + + if (toggle) { + toggle.addEventListener("click", function () { + setOpen(!open); + }); + } + + if (backTop) { + backTop.addEventListener("click", function (e) { + e.preventDefault(); + window.scrollTo({ + top: 0, + behavior: "smooth", + }); + }); + } + + if (backBtn) { + backBtn.addEventListener("click", function (e) { + e.preventDefault(); + window.history.back(); + }); + } + } + + /* ---------- Init ---------- */ + + document.addEventListener("DOMContentLoaded", function () { + initThemeFromDOM(); + initThemeToggle(); + initMobileNav(); + initDock(); + }); +})(); diff --git a/themes/minimal-black/static/js/search.js b/themes/minimal-black/static/js/search.js new file mode 100644 index 0000000..68496c2 --- /dev/null +++ b/themes/minimal-black/static/js/search.js @@ -0,0 +1,253 @@ +(function () { + var overlay, inputEl, resultsEl; + var indexLoaded = false; + var pages = []; + + function ensureElements() { + if (!overlay) { + overlay = document.querySelector("[data-search-overlay]"); + } + if (!inputEl && overlay) { + inputEl = overlay.querySelector("[data-search-input]"); + } + if (!resultsEl && overlay) { + resultsEl = overlay.querySelector("[data-search-results]"); + } + } + + function loadIndex() { + if (indexLoaded) return; + indexLoaded = true; + + fetch("/index.json") + .then(function (r) { + if (!r.ok) throw new Error("index.json not found"); + return r.json(); + }) + .then(function (data) { + pages = (data && data.pages) || []; + }) + .catch(function () { + pages = []; + }); + } + + function openOverlay() { + ensureElements(); + if (!overlay) return; + + overlay.classList.remove("search-overlay"); + overlay.classList.add("search-overlay--open"); + loadIndex(); + + if (inputEl) { + setTimeout(function () { + inputEl.focus(); + }, 20); + } + } + + function closeOverlay() { + ensureElements(); + if (!overlay) return; + + if (overlay.classList.contains("search-overlay--closing")) return; + + overlay.classList.add("search-overlay--closing"); + + setTimeout(function () { + overlay.classList.remove("search-overlay--open"); + overlay.classList.remove("search-overlay--closing"); + overlay.classList.add("search-overlay"); + + if (inputEl) inputEl.value = ""; + + if (resultsEl) { + resultsEl.innerHTML = + '
' + + '
' + + '

Start searching

' + + '

Enter keywords to search articles.

' + + "
"; + } + }, 180); + } + + function filterPages(query) { + if (!pages.length) return []; + var q = (query || "").toLowerCase().trim(); + if (!q) return []; + return pages + .filter(function (p) { + var t = (p.title || "").toLowerCase(); + var s = (p.summary || "").toLowerCase(); + return t.indexOf(q) !== -1 || s.indexOf(q) !== -1; + }) + .slice(0, 20); + } + + function highlightText(text, query) { + if (!query) return text; + var regex = new RegExp("(" + query.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + ")", "gi"); + return text.replace(regex, '$1'); + } + + function getSectionIcon(section) { + var icons = { + blog: "fa-regular fa-note-sticky", + projects: "fa-regular fa-folder-open", + posts: "fa-regular fa-note-sticky", + }; + return icons[section.toLowerCase()] || "fa-regular fa-file"; + } + + function truncateText(text, maxLength) { + if (!text || text.length <= maxLength) return text; + return text.substring(0, maxLength) + "..."; + } + + function renderResults(query) { + ensureElements(); + if (!resultsEl) return; + + var q = (query || "").trim(); + if (!q) { + resultsEl.innerHTML = + '
' + + '
' + + '

Start searching

' + + '

Enter keywords to search articles.

' + + "
"; + return; + } + + var matches = filterPages(q); + if (!matches.length) { + resultsEl.innerHTML = + '
' + + '
' + + '

No results found

' + + '

Try different keywords or check your spelling.

' + + "
"; + return; + } + + var html = matches + .map(function (p, index) { + var title = highlightText(p.title || "Untitled", q); + var section = p.section || ""; + var summary = truncateText(p.summary || "", 120); + var highlightedSummary = highlightText(summary, q); + var icon = getSectionIcon(section); + var date = p.date ? new Date(p.date).toLocaleDateString("en-US", { + year: "numeric", + month: "short", + day: "numeric" + }) : ""; + + return ( + '' + + '
' + + '' + + '
' + + '
' + + title + + "
" + + '
' + + (section ? '' + section + "" : "") + + (date ? '' + date + "" : "") + + "
" + + "
" + + "
" + + (highlightedSummary ? '
' + highlightedSummary + "
" : "") + + "
" + ); + }) + .join(""); + + resultsEl.innerHTML = html; + + // Add keyboard navigation + addKeyboardNavigation(); + } + + var selectedIndex = -1; + + function addKeyboardNavigation() { + ensureElements(); + if (!inputEl) return; + + var items = resultsEl.querySelectorAll(".search-result-item"); + + inputEl.addEventListener("keydown", function(e) { + if (e.key === "ArrowDown") { + e.preventDefault(); + selectedIndex = Math.min(selectedIndex + 1, items.length - 1); + updateSelection(items); + } else if (e.key === "ArrowUp") { + e.preventDefault(); + selectedIndex = Math.max(selectedIndex - 1, -1); + updateSelection(items); + } else if (e.key === "Enter" && selectedIndex >= 0) { + e.preventDefault(); + items[selectedIndex].click(); + } + }); + } + + function updateSelection(items) { + items.forEach(function(item, index) { + if (index === selectedIndex) { + item.classList.add("search-result-item--selected"); + item.scrollIntoView({ block: "nearest", behavior: "smooth" }); + } else { + item.classList.remove("search-result-item--selected"); + } + }); + } + + function initSearch() { + ensureElements(); + if (!overlay) return; + + // Close and ESC + overlay.querySelectorAll("[data-search-close]").forEach(function (el) { + el.addEventListener("click", closeOverlay); + }); + + document.addEventListener("keydown", function (e) { + if (e.key === "Escape") closeOverlay(); + }); + + // Typing + if (inputEl) { + inputEl.addEventListener("input", function (e) { + renderResults(e.target.value || ""); + }); + } + + // Ctrl/Cmd + K to open + document.addEventListener("keydown", function (e) { + if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "k") { + e.preventDefault(); + openOverlay(); + } + }); + + // Expose global API for inline onclick + window.MinimalSearch = { + open: openOverlay, + close: closeOverlay, + }; + } + + if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", initSearch); + } else { + initSearch(); + } +})(); diff --git a/themes/minimal-black/tailwind.config.js b/themes/minimal-black/tailwind.config.js new file mode 100644 index 0000000..359a385 --- /dev/null +++ b/themes/minimal-black/tailwind.config.js @@ -0,0 +1,25 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + darkMode: ["class", '[data-theme="dark"]'], + content: [ + "./layouts/**/*.html", + "./exampleSite/content/**/*.{md,html}", + "../../content/**/*.{md,html}", + ], + theme: { + extend: { + colors: { + bg: "var(--color-bg)", + surface: "var(--color-surface)", + text: "var(--color-text)", + muted: "var(--color-text-muted)", + border: "var(--color-border)", + accent: "var(--color-accent)", + }, + maxWidth: { + "3xl": "48rem", + }, + }, + }, + plugins: [require("@tailwindcss/typography")], +}; diff --git a/themes/minimal-black/theme.toml b/themes/minimal-black/theme.toml new file mode 100644 index 0000000..300d3f7 --- /dev/null +++ b/themes/minimal-black/theme.toml @@ -0,0 +1,29 @@ +name = "Minimal Black" +license = "MIT" +licenselink = "https://gitlab.com/jimchr12/hugo-minimal-black/blob/main/LICENSE" +description = "A minimal, dark-mode first Hugo theme with true black backgrounds, purple accents, and comprehensive content styling. Features responsive design, search, TOC, and multiple page layouts." +homepage = "https://gitlab.com/jimchr12/hugo-minimal-black" +demosite = "https://minimal-black-demo.netlify.app" +tags = ["blog", "portfolio", "minimal", "dark", "tailwind", "responsive", "clean", "modern", "personal"] +features = [ + "Dark Mode", + "Light Mode", + "Responsive Design", + "Search", + "Table of Contents", + "Syntax Highlighting", + "Google Analytics", + "Font Awesome Icons", + "Devicon Support", + "Multiple About Page Layouts", + "Project Portfolio", + "Blog", + "Gallery Shortcode", + "Mermaid Diagrams", + "GitHub Flavored Markdown" +] +min_version = "0.120.0" + +[author] + name = "Jim Christopoulos" + homepage = "https://jimchristopoulos.com/"