Init Commit
@@ -0,0 +1,13 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: [zetxek] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
||||
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
||||
@@ -0,0 +1,40 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: "[BUG]"
|
||||
labels: bug
|
||||
assignees: zetxek
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
What browsers did you test?
|
||||
Do you have a public URL to reproduce, or a repository showing the error?
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Smartphone (please complete the following information):**
|
||||
- Device: [e.g. iPhone6]
|
||||
- OS: [e.g. iOS8.1]
|
||||
- Browser [e.g. stock browser, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
name: Setup issue
|
||||
about: Trouble getting your own version of the theme running
|
||||
title: "[setup]"
|
||||
labels: setup
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## Link to your source code
|
||||
|
||||
https://github.com/...
|
||||
|
||||
_this helps understand even better the context, and in some occasions will make it possible to get a PR to get the issues fixed_
|
||||
|
||||
_if your code is not publicly available it will be less prioritized to give support - as it will not be as beneficial for the community_
|
||||
|
||||
## What do you want to achieve?
|
||||
|
||||
_describe what you are trying to do_
|
||||
|
||||
## What have you tried so far?
|
||||
|
||||
_describe what steps you have taken: have you created your site, are you customizing the theme, which version are you using..._
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: "[IDEA]"
|
||||
labels: enhancement
|
||||
assignees: zetxek
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
@@ -0,0 +1,214 @@
|
||||
# Adritian Hugo Theme - AI Coding Instructions
|
||||
|
||||
## Project Overview
|
||||
|
||||
This is a **Hugo theme** for personal websites/portfolios, not a standalone site (although a example site is provided, to demonstrate the usage of the theme itself). The theme provides layouts, styling, and functionality that users import via Hugo modules or as a git submodule.
|
||||
|
||||
**Technology Stack:**
|
||||
- **Static Site Generator:** Hugo (v0.136+ extended version required)
|
||||
- **CSS Framework:** Bootstrap 5 (v5.3.8)
|
||||
- **CSS Preprocessor:** SCSS/Sass
|
||||
- **JavaScript Libraries:** Fuse.js (search), DOMPurify (XSS protection)
|
||||
- **Testing:** Playwright (E2E tests)
|
||||
- **Package Manager:** npm
|
||||
- **Templating:** Hugo Templates (Go templates)
|
||||
- **Build Tools:** Hugo's built-in asset pipeline, PostCSS, Autoprefixer
|
||||
|
||||
## Project Architecture
|
||||
|
||||
This is a **Hugo theme** for personal websites/portfolios, not a standalone site. The theme provides layouts, styling, and functionality that users import via Hugo modules or as a git submodule.
|
||||
|
||||
The theme provides a example site (`exampleSite/`) that demonstrates all features and serves as a starting point for users. It is also used for the integration tests.
|
||||
|
||||
**Key Structure:**
|
||||
- `layouts/` - Hugo templates (partials, shortcodes, content types)
|
||||
- `assets/scss/` - Bootstrap 5-based styling with theme customizations
|
||||
- `exampleSite/` - Demo content that gets distributed to users via `adritian-theme-helper` and serves as a github template repository
|
||||
- `archetypes/` - Content templates for different content types
|
||||
- `i18n/` - Multilingual translations (en/es/fr)
|
||||
|
||||
## Content Architecture & Custom Types
|
||||
|
||||
The theme implements **5 custom content types** with specific frontmatter:
|
||||
|
||||
1. **Experience** (`content/experience/`) - Job history with `jobTitle`, `company`, `location`, `duration`
|
||||
2. **Skills** (`content/skills/`) - Technical skills with progress bars and categories
|
||||
3. **Blog** (`content/blog/`) - Posts with sidebar/default layouts
|
||||
4. **Client-work** (`content/client-work/`) - Portfolio pieces
|
||||
5. **Testimonial** (`content/testimonial/`) - User testimonials
|
||||
|
||||
**Critical:** Each content type has a corresponding archetype in `archetypes/` and layout in `layouts/`. Always maintain this trio when adding new types.
|
||||
|
||||
## Development Workflows
|
||||
|
||||
### Local Development
|
||||
```bash
|
||||
# Install dependencies
|
||||
npm install # Installs Bootstrap + theme-helper
|
||||
# Theme development (testing with exampleSite)
|
||||
npm run serve
|
||||
```
|
||||
|
||||
### Testing
|
||||
```bash
|
||||
npm test # Full test suite
|
||||
npm run test:e2e # Playwright E2E tests
|
||||
npm run test:e2e:no-menus # Tests without navigation (for menu-less configs)
|
||||
```
|
||||
|
||||
**Test Coverage:** E2E tests validate theme switching, language switching, content rendering, search functionality, and HTML validation across all major features.
|
||||
|
||||
### Performance Optimization
|
||||
```bash
|
||||
npm run build:performance # Optimized build with critical CSS
|
||||
node scripts/optimize-performance.js # Performance analysis
|
||||
```
|
||||
|
||||
## Styling System
|
||||
|
||||
**SCSS Architecture:** Modular approach extending Bootstrap 5
|
||||
- `assets/scss/adritian.scss` - Main entry point importing all partials
|
||||
- `assets/scss/_variables.scss` - Theme color/sizing variables
|
||||
- Individual component files: `_navbar.scss`, `_blog.scss`, `_experience.scss`, etc.
|
||||
|
||||
**Theme Colors:** Uses CSS custom properties with `$base-color` as primary. Dark/light themes switch via CSS variables, not separate stylesheets.
|
||||
|
||||
## Multilingual (i18n) Implementation
|
||||
|
||||
**Translation Strategy:**
|
||||
- Static strings: `i18n/*.yaml` files (navigation, UI elements)
|
||||
- Content: Hugo's file-based translation (`content/en/`, `content/es/`, etc.)
|
||||
- **Post-v1.7.0:** Prefers shortcode-based content over i18n strings for flexibility
|
||||
|
||||
## Shortcode System
|
||||
|
||||
**Primary Content Method:** Pages built with shortcodes rather than traditional Hugo sections.
|
||||
|
||||
Key shortcodes in `layouts/shortcodes/`:
|
||||
- `experience-section` - Displays limited experience list (controlled by `homepageExperienceCount`)
|
||||
- `experience-list` - Print-friendly experience list
|
||||
- `contact-section` - Formspree integration
|
||||
- `showcase-section` - Portfolio display
|
||||
- `about-section`, `text-section` - Content blocks
|
||||
|
||||
**Pattern:** Most homepage sections use shortcodes for maximum customization flexibility.
|
||||
|
||||
## Module Distribution Model
|
||||
|
||||
**Critical Understanding:** This theme is distributed via:
|
||||
1. **Hugo Module** (recommended) - `hugo mod get github.com/zetxek/adritian-free-hugo-theme`
|
||||
2. **Theme Helper Package** - `@zetxek/adritian-theme-helper` downloads demo content
|
||||
3. **Git Submodule** (legacy) - Direct theme inclusion
|
||||
|
||||
**Bootstrap Process:**
|
||||
```bash
|
||||
hugo mod get -u github.com/zetxek/adritian-free-hugo-theme
|
||||
hugo mod npm pack
|
||||
npm install # Installs Bootstrap + theme-helper
|
||||
./node_modules/@zetxek/adritian-theme-helper/dist/scripts/download-content.js
|
||||
```
|
||||
|
||||
## Configuration Patterns
|
||||
|
||||
**Hugo Config (`hugo.toml`):**
|
||||
- Performance optimizations: `[minify]`, `[imaging]`, `[markup]` sections
|
||||
- Custom output formats for content types (footer content excluded from HTML generation)
|
||||
- Module imports section for theme loading
|
||||
|
||||
**Package.json Scripts:**
|
||||
- `serve` - Development with exampleSite
|
||||
- `build` - Production build with minification
|
||||
- Separate test commands for different scenarios
|
||||
|
||||
## Key Integration Points
|
||||
|
||||
**Search:** Powered by Fuse.js with JSON index generation
|
||||
**Contact Forms:** Formspree integration via shortcode
|
||||
**Analytics:** Vercel Analytics support built-in
|
||||
**Performance:** Critical CSS generation, image optimization, minification
|
||||
|
||||
## Common Development Pitfalls
|
||||
|
||||
1. **Missing Dependencies:** Theme requires Bootstrap via npm - users often miss `npm install` step
|
||||
2. **Content Structure:** Custom content types require specific frontmatter fields - validate against archetypes
|
||||
3. **Translation Scope:** Post-v1.7.0 prefers shortcode content over i18n strings
|
||||
4. **Module vs Submodule:** Most issues stem from incorrect installation method
|
||||
|
||||
## Testing Philosophy
|
||||
|
||||
E2E tests validate real user workflows rather than individual components. Tests include theme switching, language switching, navigation, search, and content rendering across different configurations (with/without menus).
|
||||
|
||||
When modifying layouts or adding features, ensure corresponding E2E tests exist in `tests/e2e/`.
|
||||
|
||||
## Coding Standards and Conventions
|
||||
|
||||
### HTML/Hugo Templates
|
||||
- Use semantic HTML5 elements for proper document structure
|
||||
- Follow Hugo's template naming conventions (lowercase with hyphens)
|
||||
- Partial templates should be in `layouts/partials/`
|
||||
- Shortcodes should be in `layouts/shortcodes/`
|
||||
- Use Hugo's built-in functions when available (e.g., `absURL`, `relURL`)
|
||||
|
||||
### SCSS/CSS
|
||||
- Follow Bootstrap 5 conventions and utility classes where possible
|
||||
- Use BEM naming methodology for custom components when needed
|
||||
- Prefix custom variables with theme-specific identifiers
|
||||
- Keep files modular - one component per file
|
||||
- Use CSS custom properties for themeable values
|
||||
- File naming: lowercase with underscores (e.g., `_navbar.scss`, `_blog.scss`)
|
||||
|
||||
### JavaScript
|
||||
- Use vanilla JavaScript (no jQuery)
|
||||
- Prefer modern ES6+ syntax
|
||||
- Keep JavaScript minimal - theme is primarily CSS-driven
|
||||
- Use DOMPurify for any user-generated content sanitization
|
||||
- Ensure all JavaScript is progressively enhanced (works without JS when possible)
|
||||
|
||||
### Content and Configuration
|
||||
- YAML frontmatter for content files
|
||||
- TOML for Hugo configuration files
|
||||
- Follow existing frontmatter patterns for each content type
|
||||
- Use descriptive, human-readable keys
|
||||
|
||||
### File Naming Conventions
|
||||
- Templates: lowercase with hyphens (e.g., `single.html`, `list.html`)
|
||||
- SCSS files: lowercase with underscores, prefixed with `_` for partials
|
||||
- Content files: lowercase with hyphens (e.g., `my-blog-post.md`)
|
||||
- Archetypes: match content type name (e.g., `blog.md`, `experience.md`)
|
||||
|
||||
## Web Accessibility Requirements
|
||||
|
||||
**This theme prioritizes accessibility (WCAG 2.1 AA compliance):**
|
||||
|
||||
- All interactive elements must be keyboard accessible
|
||||
- Proper ARIA labels and roles for non-semantic elements
|
||||
- Color contrast ratios must meet WCAG AA standards (4.5:1 for normal text, 3:1 for large text)
|
||||
- Images must have descriptive `alt` attributes
|
||||
- Forms must have associated labels
|
||||
- Focus indicators must be visible and clear
|
||||
- Dark/light theme switching must maintain accessibility standards in both modes
|
||||
- Skip navigation links for keyboard users
|
||||
- Semantic HTML structure (proper heading hierarchy, landmarks, etc.)
|
||||
- Test changes with screen readers when modifying navigation or interactive elements
|
||||
|
||||
## Security Best Practices
|
||||
|
||||
- Never commit secrets, API keys, or sensitive data
|
||||
- Use DOMPurify for sanitizing any user-generated or external content
|
||||
- Validate and sanitize all user inputs in contact forms
|
||||
- Follow Hugo's security best practices for template rendering
|
||||
- Keep dependencies updated to patch security vulnerabilities
|
||||
- Use Hugo's built-in `safeHTML`, `safeCSS`, `safeJS` functions appropriately
|
||||
|
||||
## Files and Directories to Ignore
|
||||
|
||||
Copilot should **ignore** or be cautious with:
|
||||
- `node_modules/` - Third-party dependencies
|
||||
- `public/` - Hugo build output (generated files)
|
||||
- `resources/` - Hugo's asset cache
|
||||
- `.hugo_build.lock` - Hugo build lock file
|
||||
- `package-lock.json` - npm lock file (only update via npm commands)
|
||||
- `.git/` - Version control directory
|
||||
- `exampleSite/public/` - Example site build output
|
||||
- `exampleSite/resources/` - Example site cache
|
||||
- `.DS_Store` - macOS system files
|
||||
@@ -0,0 +1,18 @@
|
||||
# To get started with Dependabot version updates, you'll need to specify which
|
||||
# package ecosystems to update and where the package manifests are located.
|
||||
# Please see the documentation for all configuration options:
|
||||
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "npm" # See documentation for possible values
|
||||
directory: "/" # Location of package manifests
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
day: "friday"
|
||||
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/.github/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
day: "friday"
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"ci": {
|
||||
"collect": {
|
||||
"staticDistDir": "./built-site"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
name: Update contributors
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
id-token: write
|
||||
pull-requests: read
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
contrib-readme-job:
|
||||
runs-on: ubuntu-latest
|
||||
name: Readme contributors
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Contribute List
|
||||
uses: akhilmhdh/contributors-readme-action@v2.3.10
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -0,0 +1,46 @@
|
||||
name: Crease release
|
||||
permissions:
|
||||
contents: write
|
||||
id-token: write
|
||||
pages: write
|
||||
pull-requests: write
|
||||
checks: write
|
||||
statuses: write
|
||||
deployments: write
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*.*.*"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v4
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v4
|
||||
|
||||
- name: Install Go dependencies
|
||||
run: go mod download
|
||||
|
||||
- name: Install NPM dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Get version
|
||||
id: get_version
|
||||
run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Create ZIP archive
|
||||
run: zip -r "adritian-free-hugo-theme-${{ steps.get_version.outputs.VERSION }}.zip" . -x "*.git*"
|
||||
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
generate_release_notes: true
|
||||
files: "adritian-free-hugo-theme-${{ steps.get_version.outputs.VERSION }}.zip"
|
||||
|
||||
@@ -0,0 +1,231 @@
|
||||
# Workflow to verify that the exampleSite runs ok
|
||||
name: Test example site
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
pages: write
|
||||
pull-requests: write
|
||||
checks: write
|
||||
statuses: write
|
||||
on:
|
||||
# Runs on pull requests targeting the default branch
|
||||
pull_request:
|
||||
branches: ["main"]
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
env:
|
||||
CI: true
|
||||
|
||||
jobs:
|
||||
# Prepare dependencies job
|
||||
prepare:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
HUGO_VERSION: ${{ vars.HUGO_VERSION || '0.147.2' }}
|
||||
steps:
|
||||
- name: Install Hugo CLI
|
||||
run: |
|
||||
wget -O ${{ runner.temp }}/hugo.deb https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb \
|
||||
&& sudo dpkg -i ${{ runner.temp }}/hugo.deb
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: lts/*
|
||||
|
||||
- name: Cache Node Modules
|
||||
id: cache-node-modules
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
node_modules
|
||||
key: modules-${{ hashFiles('package-lock.json') }}
|
||||
|
||||
- name: Cache Playwright Browsers
|
||||
id: cache-playwright
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cache/ms-playwright
|
||||
key: playwright-${{ hashFiles('package-lock.json') }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Install Playwright browsers
|
||||
if: steps.cache-playwright.outputs.cache-hit != 'true'
|
||||
run: npx playwright install --with-deps
|
||||
|
||||
# Test with no menus
|
||||
test-no-menus:
|
||||
needs: prepare
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
HUGO_VERSION: ${{ vars.HUGO_VERSION || '0.147.2' }}
|
||||
steps:
|
||||
- name: Install Hugo CLI
|
||||
run: |
|
||||
wget -O ${{ runner.temp }}/hugo.deb https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb \
|
||||
&& sudo dpkg -i ${{ runner.temp }}/hugo.deb
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: lts/*
|
||||
|
||||
- name: Restore Node Modules
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
path: |
|
||||
node_modules
|
||||
key: modules-${{ hashFiles('package-lock.json') }}
|
||||
|
||||
- name: Restore Playwright Browsers
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cache/ms-playwright
|
||||
key: playwright-${{ hashFiles('package-lock.json') }}
|
||||
|
||||
- name: Build site (no menus)
|
||||
run: |
|
||||
cd exampleSite
|
||||
hugo server --themesDir ../.. --buildDrafts --buildFuture --bind 0.0.0.0 --config hugo.toml,hugo.disablemenu.toml &
|
||||
sleep 10
|
||||
|
||||
- name: Run Playwright tests (no menus)
|
||||
run: npm run test:e2e:no-menus
|
||||
|
||||
- name: Get the failure count
|
||||
id: tests
|
||||
run: |
|
||||
export count=$(cat ./playwright-report/result.txt | grep Failed | cut -d ':' -f 2 | tr -d ' ')
|
||||
echo "fail_count=$count" >> "$GITHUB_OUTPUT"
|
||||
echo $count
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: ${{ !cancelled() }}
|
||||
with:
|
||||
name: playwright-report-no-menus
|
||||
path: playwright-report/
|
||||
retention-days: 30
|
||||
outputs:
|
||||
fail_count: ${{ steps.tests.outputs.fail_count }}
|
||||
|
||||
# Test with menus
|
||||
test-with-menus:
|
||||
needs: prepare
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
HUGO_VERSION: ${{ vars.HUGO_VERSION || '0.147.2' }}
|
||||
steps:
|
||||
- name: Install Hugo CLI
|
||||
run: |
|
||||
wget -O ${{ runner.temp }}/hugo.deb https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb \
|
||||
&& sudo dpkg -i ${{ runner.temp }}/hugo.deb
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: lts/*
|
||||
|
||||
- name: Restore Node Modules
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
path: |
|
||||
node_modules
|
||||
key: modules-${{ hashFiles('package-lock.json') }}
|
||||
|
||||
- name: Restore Playwright Browsers
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cache/ms-playwright
|
||||
key: playwright-${{ hashFiles('package-lock.json') }}
|
||||
|
||||
- name: Build and serve demo site (with menus)
|
||||
run: |
|
||||
cd exampleSite
|
||||
hugo server --themesDir ../.. --buildDrafts --buildFuture --bind 0.0.0.0 &
|
||||
sleep 10
|
||||
|
||||
- name: Run Playwright tests (with menus)
|
||||
run: npm run test:e2e
|
||||
|
||||
- name: Get the failure count
|
||||
id: tests
|
||||
run: |
|
||||
export count=$(cat ./playwright-report/result.txt | grep Failed | cut -d ':' -f 2 | tr -d ' ')
|
||||
echo "fail_count=$count" >> "$GITHUB_OUTPUT"
|
||||
echo $count
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: ${{ !cancelled() }}
|
||||
with:
|
||||
name: playwright-report-with-menus
|
||||
path: playwright-report/
|
||||
retention-days: 30
|
||||
outputs:
|
||||
fail_count: ${{ steps.tests.outputs.fail_count }}
|
||||
|
||||
# Combine reports and publish
|
||||
publish-reports:
|
||||
if: github.ref == 'refs/heads/main'
|
||||
continue-on-error: true
|
||||
needs: [test-no-menus, test-with-menus]
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pages: write
|
||||
id-token: write
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
steps:
|
||||
- name: Download no-menus report
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: playwright-report-no-menus
|
||||
path: playwright-report-no-menus
|
||||
|
||||
- name: Download with-menus report
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: playwright-report-with-menus
|
||||
path: playwright-report-with-menus
|
||||
|
||||
- name: Combine reports
|
||||
run: |
|
||||
mkdir -p combined-report
|
||||
cp -r playwright-report-no-menus/* combined-report/
|
||||
mkdir -p combined-report/with-menus
|
||||
cp -r playwright-report-with-menus/* combined-report/with-menus/
|
||||
|
||||
- name: Upload combined artifact for pages
|
||||
if: always()
|
||||
uses: actions/upload-pages-artifact@v3
|
||||
with:
|
||||
path: "./combined-report"
|
||||
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v4
|
||||
|
||||
- name: Check for test failures
|
||||
if: ${{ needs.test-no-menus.outputs.fail_count != '0' || needs.test-with-menus.outputs.fail_count != '0' }}
|
||||
run: exit 1
|
||||
@@ -0,0 +1,443 @@
|
||||
name: Module Init, HTML and Lighthouse Check
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
pull-requests: write
|
||||
checks: write
|
||||
statuses: write
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
|
||||
jobs:
|
||||
test-module-init:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Hugo
|
||||
uses: peaceiris/actions-hugo@v3
|
||||
with:
|
||||
hugo-version: ${{ vars.HUGO_VERSION || 'latest' }}
|
||||
extended: true
|
||||
|
||||
- name: Create test site
|
||||
run: |
|
||||
hugo new site test-site
|
||||
cd test-site
|
||||
|
||||
- name: Initialize Hugo module
|
||||
working-directory: ./test-site
|
||||
run: |
|
||||
hugo mod init github.com/zetxek/test-site
|
||||
hugo mod get -u github.com/zetxek/adritian-free-hugo-theme
|
||||
|
||||
- name: Configure module ref
|
||||
if: github.ref != 'refs/heads/main'
|
||||
working-directory: ./test-site
|
||||
run: |
|
||||
# Use the branch that initiated the PR
|
||||
branch_name=${GITHUB_HEAD_REF}
|
||||
echo "Using branch: ${branch_name}"
|
||||
hugo mod get -u github.com/zetxek/adritian-free-hugo-theme@${branch_name}
|
||||
|
||||
- name: Configure Hugo module in hugo.toml
|
||||
working-directory: ./test-site
|
||||
run: |
|
||||
echo '[module]
|
||||
[[module.imports]]
|
||||
path = "github.com/zetxek/adritian-free-hugo-theme"' > hugo.toml
|
||||
|
||||
- name: Validate Hugo module configuration
|
||||
working-directory: ./test-site
|
||||
run: |
|
||||
hugo mod verify
|
||||
hugo mod graph
|
||||
|
||||
- name: Upload test site
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: test-site
|
||||
path: test-site
|
||||
retention-days: 1
|
||||
|
||||
build-site:
|
||||
needs: test-module-init
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Hugo
|
||||
uses: peaceiris/actions-hugo@v3
|
||||
with:
|
||||
hugo-version: 'latest'
|
||||
extended: true
|
||||
|
||||
- name: Download test site
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: test-site
|
||||
path: test-site
|
||||
|
||||
- name: Pack dependencies
|
||||
working-directory: ./test-site
|
||||
run: |
|
||||
hugo mod npm pack
|
||||
|
||||
- name: (debug) Cat package.json
|
||||
working-directory: ./test-site
|
||||
run: |
|
||||
cat package.json
|
||||
|
||||
- name: Npm install (bootstrap)
|
||||
working-directory: ./test-site
|
||||
run: |
|
||||
npm install
|
||||
|
||||
- name: (debug) ls node_modules
|
||||
working-directory: ./test-site
|
||||
run: |
|
||||
ls node_modules
|
||||
|
||||
- name: Run helper script (copy config and content)
|
||||
working-directory: ./test-site
|
||||
run: |
|
||||
./node_modules/@zetxek/adritian-theme-helper/dist/scripts/download-content.js
|
||||
|
||||
- name: Run build
|
||||
working-directory: ./test-site
|
||||
run: hugo
|
||||
|
||||
- name: Upload built site
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: built-site
|
||||
path: test-site/public
|
||||
retention-days: 1
|
||||
|
||||
validate-html:
|
||||
needs: build-site
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download built site
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: built-site
|
||||
path: ./public
|
||||
|
||||
- name: Print index.html
|
||||
working-directory: ./public
|
||||
run: |
|
||||
cat index.html
|
||||
|
||||
# Validate HTML syntax with https://github.com/marketplace/actions/html5-validator
|
||||
- name: Validate HTML syntax
|
||||
uses: Cyb3r-Jak3/html5validator-action@v7.2.0
|
||||
with:
|
||||
root: ./public
|
||||
skip_git_check: true
|
||||
|
||||
lighthouse-check:
|
||||
needs: build-site
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Download built site
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: built-site
|
||||
path: ./public
|
||||
|
||||
- name: Create output directory
|
||||
run: mkdir -p ${{ github.workspace }}/tmp/artifacts
|
||||
|
||||
- name: Start local server
|
||||
run: |
|
||||
npx http-server ./public -p 8080 &
|
||||
SERVER_PID=$!
|
||||
echo "Started http-server with PID: $SERVER_PID"
|
||||
|
||||
# Wait for server to be ready (max 30 seconds)
|
||||
for i in {1..30}; do
|
||||
if curl -s http://localhost:8080 > /dev/null 2>&1; then
|
||||
echo "✅ Server is ready and responding"
|
||||
break
|
||||
fi
|
||||
echo "Waiting for server to start... ($i/30)"
|
||||
sleep 1
|
||||
done
|
||||
|
||||
# Verify server is actually responding
|
||||
if ! curl -s http://localhost:8080 > /dev/null 2>&1; then
|
||||
echo "❌ Server failed to start properly"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Server is ready, proceeding with Lighthouse check"
|
||||
|
||||
- name: Lighthouse Check
|
||||
id: lighthouse
|
||||
uses: foo-software/lighthouse-check-action@v12.0.1
|
||||
with:
|
||||
device: 'all'
|
||||
gitHubAccessToken: ${{ secrets.GITHUB_TOKEN }}
|
||||
prCommentEnabled: false
|
||||
urls: 'http://localhost:8080'
|
||||
outputDirectory: ${{ github.workspace }}/tmp/artifacts
|
||||
|
||||
- name: Extract and save Lighthouse scores
|
||||
id: extract-scores
|
||||
run: |
|
||||
# Extract scores from Lighthouse reports
|
||||
mkdir -p .github
|
||||
|
||||
results_file="${{ github.workspace }}/tmp/artifacts/results.json"
|
||||
|
||||
if [ ! -f "$results_file" ]; then
|
||||
echo "❌ Error: results.json not found"
|
||||
echo '{"timestamp":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","commit":"${{ github.sha }}","scores":{}}' > current-scores.json
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "✅ Found results.json, extracting scores..."
|
||||
|
||||
echo "{" > current-scores.json
|
||||
echo ' "timestamp": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'",' >> current-scores.json
|
||||
echo ' "commit": "${{ github.sha }}",' >> current-scores.json
|
||||
echo ' "scores": {' >> current-scores.json
|
||||
|
||||
# The results.json from lighthouse-check-action contains an array of results
|
||||
# Scores are already 0-100, not 0-1
|
||||
num_results=$(jq 'length' "$results_file")
|
||||
|
||||
first_device=true
|
||||
|
||||
# Loop through results and organize by emulatedFormFactor
|
||||
for i in $(seq 0 $((num_results - 1))); do
|
||||
form_factor=$(jq -r ".[$i].emulatedFormFactor // \"unknown\"" "$results_file")
|
||||
|
||||
perf=$(jq -r ".[$i].scores.performance // 0" "$results_file")
|
||||
a11y=$(jq -r ".[$i].scores.accessibility // 0" "$results_file")
|
||||
bp=$(jq -r ".[$i].scores.bestPractices // 0" "$results_file")
|
||||
seo=$(jq -r ".[$i].scores.seo // 0" "$results_file")
|
||||
|
||||
echo " 📱 $form_factor: Performance=$perf, Accessibility=$a11y, Best Practices=$bp, SEO=$seo"
|
||||
|
||||
if [ "$form_factor" = "mobile" ] || [ "$form_factor" = "desktop" ]; then
|
||||
if [ "$first_device" = false ]; then
|
||||
echo "," >> current-scores.json
|
||||
fi
|
||||
first_device=false
|
||||
|
||||
echo " \"$form_factor\": {" >> current-scores.json
|
||||
echo " \"performance\": $perf," >> current-scores.json
|
||||
echo " \"accessibility\": $a11y," >> current-scores.json
|
||||
echo " \"best-practices\": $bp," >> current-scores.json
|
||||
echo " \"seo\": $seo" >> current-scores.json
|
||||
echo -n " }" >> current-scores.json
|
||||
fi
|
||||
done
|
||||
|
||||
echo "" >> current-scores.json
|
||||
echo " }" >> current-scores.json
|
||||
echo "}" >> current-scores.json
|
||||
|
||||
echo "✅ Scores extracted successfully"
|
||||
|
||||
- name: Load baseline scores from lighthouse-data branch
|
||||
id: load-baseline
|
||||
if: github.event_name == 'pull_request'
|
||||
run: |
|
||||
# Try to fetch baseline scores from lighthouse-data branch
|
||||
if git show origin/lighthouse-data:.github/lighthouse-scores.json > baseline-scores.json 2>/dev/null; then
|
||||
echo "baseline_exists=true" >> $GITHUB_OUTPUT
|
||||
baseline_commit=$(jq -r '.commit // ""' baseline-scores.json)
|
||||
echo "✅ Baseline scores loaded from lighthouse-data branch (commit: ${baseline_commit:0:7})"
|
||||
else
|
||||
echo "baseline_exists=false" >> $GITHUB_OUTPUT
|
||||
echo "ℹ️ No baseline found - this will be the first baseline after merge"
|
||||
# Create empty baseline
|
||||
echo '{"commit":"","scores":{}}' > baseline-scores.json
|
||||
fi
|
||||
|
||||
- name: Format Lighthouse results with comparison
|
||||
id: format-results
|
||||
run: |
|
||||
# Function to get score emoji
|
||||
get_emoji() {
|
||||
score=$1
|
||||
# Handle empty or invalid scores
|
||||
if [ -z "$score" ] || [ "$score" = "0" ] || [ "$score" = "null" ]; then
|
||||
echo "⚪"
|
||||
return
|
||||
fi
|
||||
if (( $(echo "$score >= 90" | bc -l) )); then
|
||||
echo "🟢"
|
||||
elif (( $(echo "$score >= 50" | bc -l) )); then
|
||||
echo "🟠"
|
||||
else
|
||||
echo "🔴"
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to get delta indicator
|
||||
get_delta() {
|
||||
current=$1
|
||||
baseline=$2
|
||||
if [ "$baseline" = "null" ] || [ -z "$baseline" ] || [ "$baseline" = "0" ]; then
|
||||
echo ""
|
||||
return
|
||||
fi
|
||||
delta=$((current - baseline))
|
||||
if [ $delta -gt 0 ]; then
|
||||
echo "(↑ +$delta)"
|
||||
elif [ $delta -lt 0 ]; then
|
||||
echo "(↓ $delta)"
|
||||
else
|
||||
echo "(→)"
|
||||
fi
|
||||
}
|
||||
|
||||
# Build URLs
|
||||
commit_sha="${{ github.sha }}"
|
||||
commit_url="https://github.com/${{ github.repository }}/commit/$commit_sha"
|
||||
artifacts_url="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
|
||||
|
||||
echo "## 🔦 Lighthouse Performance Report" > comment.md
|
||||
echo "" >> comment.md
|
||||
echo "**Test Results for commit:** [\`${commit_sha:0:7}\`]($commit_url)" >> comment.md
|
||||
echo "" >> comment.md
|
||||
|
||||
# Check if we have baseline scores
|
||||
has_baseline=false
|
||||
if [ -f "baseline-scores.json" ]; then
|
||||
baseline_commit=$(jq -r '.commit // ""' baseline-scores.json)
|
||||
if [ -n "$baseline_commit" ] && [ "$baseline_commit" != "null" ]; then
|
||||
has_baseline=true
|
||||
baseline_commit_url="https://github.com/${{ github.repository }}/commit/$baseline_commit"
|
||||
echo "_Comparing against baseline from lighthouse-data branch (commit: [\`${baseline_commit:0:7}\`]($baseline_commit_url))_" >> comment.md
|
||||
echo "" >> comment.md
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check if current scores exist
|
||||
if [ ! -f "current-scores.json" ]; then
|
||||
echo "❌ **Error:** No Lighthouse scores were generated." >> comment.md
|
||||
echo "" >> comment.md
|
||||
echo "Check the workflow logs for more details." >> comment.md
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Parse and format the results
|
||||
found_scores=false
|
||||
for device in "mobile" "desktop"; do
|
||||
current_perf=$(jq -r ".scores.$device.performance // 0" current-scores.json)
|
||||
current_a11y=$(jq -r ".scores.$device.accessibility // 0" current-scores.json)
|
||||
current_bp=$(jq -r ".scores.$device[\"best-practices\"] // 0" current-scores.json)
|
||||
current_seo=$(jq -r ".scores.$device.seo // 0" current-scores.json)
|
||||
|
||||
# Check if we have any real scores (not just 0)
|
||||
if [ "$current_perf" != "0" ] || [ "$current_a11y" != "0" ] || [ "$current_bp" != "0" ] || [ "$current_seo" != "0" ]; then
|
||||
found_scores=true
|
||||
echo "### ${device^} Results" >> comment.md
|
||||
echo "" >> comment.md
|
||||
|
||||
if [ "$has_baseline" = true ]; then
|
||||
echo "| Category | Score | Change |" >> comment.md
|
||||
echo "|----------|-------|--------|" >> comment.md
|
||||
|
||||
baseline_perf=$(jq -r ".scores.$device.performance // 0" baseline-scores.json)
|
||||
baseline_a11y=$(jq -r ".scores.$device.accessibility // 0" baseline-scores.json)
|
||||
baseline_bp=$(jq -r ".scores.$device[\"best-practices\"] // 0" baseline-scores.json)
|
||||
baseline_seo=$(jq -r ".scores.$device.seo // 0" baseline-scores.json)
|
||||
|
||||
echo "| Performance | $(get_emoji $current_perf) $current_perf | $(get_delta $current_perf $baseline_perf) |" >> comment.md
|
||||
echo "| Accessibility | $(get_emoji $current_a11y) $current_a11y | $(get_delta $current_a11y $baseline_a11y) |" >> comment.md
|
||||
echo "| Best Practices | $(get_emoji $current_bp) $current_bp | $(get_delta $current_bp $baseline_bp) |" >> comment.md
|
||||
echo "| SEO | $(get_emoji $current_seo) $current_seo | $(get_delta $current_seo $baseline_seo) |" >> comment.md
|
||||
else
|
||||
echo "| Category | Score |" >> comment.md
|
||||
echo "|----------|-------|" >> comment.md
|
||||
|
||||
echo "| Performance | $(get_emoji $current_perf) $current_perf |" >> comment.md
|
||||
echo "| Accessibility | $(get_emoji $current_a11y) $current_a11y |" >> comment.md
|
||||
echo "| Best Practices | $(get_emoji $current_bp) $current_bp |" >> comment.md
|
||||
echo "| SEO | $(get_emoji $current_seo) $current_seo |" >> comment.md
|
||||
fi
|
||||
echo "" >> comment.md
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$found_scores" = false ]; then
|
||||
echo "⚠️ **Warning:** No Lighthouse scores were found in the reports." >> comment.md
|
||||
echo "" >> comment.md
|
||||
echo "This may be because:" >> comment.md
|
||||
echo "- The Lighthouse action did not generate reports" >> comment.md
|
||||
echo "- The report files have an unexpected format" >> comment.md
|
||||
echo "" >> comment.md
|
||||
fi
|
||||
|
||||
echo "📊 **[View full detailed reports in workflow artifacts]($artifacts_url)**" >> comment.md
|
||||
echo "" >> comment.md
|
||||
echo "_Report generated at $(date)_" >> comment.md
|
||||
|
||||
- name: Update PR comment
|
||||
uses: marocchino/sticky-pull-request-comment@v2
|
||||
if: github.event_name == 'pull_request'
|
||||
with:
|
||||
header: lighthouse-report
|
||||
path: comment.md
|
||||
|
||||
- name: Upload Lighthouse report
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: lighthouse-report
|
||||
path: ${{ github.workspace }}/tmp/artifacts
|
||||
|
||||
- name: Save baseline scores to lighthouse-data branch
|
||||
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
|
||||
run: |
|
||||
# Copy scores to temp location before branch switching
|
||||
# (git clean will remove it from the working directory)
|
||||
TEMP_SCORES="/tmp/lighthouse-scores-${{ github.sha }}.json"
|
||||
cp "${{ github.workspace }}/current-scores.json" "$TEMP_SCORES"
|
||||
|
||||
# Configure git
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
|
||||
# Try to fetch and checkout the lighthouse-data branch
|
||||
if git fetch origin lighthouse-data 2>/dev/null && git checkout lighthouse-data 2>/dev/null; then
|
||||
echo "✅ Checked out existing lighthouse-data branch"
|
||||
else
|
||||
echo "Creating new lighthouse-data orphan branch"
|
||||
git checkout --orphan lighthouse-data
|
||||
git rm -rf . 2>/dev/null || true
|
||||
git clean -fd 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Copy the lighthouse scores file from temp location
|
||||
mkdir -p .github
|
||||
cp "$TEMP_SCORES" .github/lighthouse-scores.json
|
||||
|
||||
# Commit and push if there are changes
|
||||
git add .github/lighthouse-scores.json
|
||||
if git diff --staged --quiet 2>/dev/null; then
|
||||
echo "No changes to lighthouse scores"
|
||||
else
|
||||
git commit -m "chore: update lighthouse baseline scores [skip ci]"
|
||||
git push origin lighthouse-data
|
||||
echo "✅ Lighthouse baseline scores updated in lighthouse-data branch"
|
||||
fi
|
||||
|
||||
# Clean up temp file
|
||||
rm -f "$TEMP_SCORES"
|
||||
@@ -0,0 +1,43 @@
|
||||
## Updates demo site (https://github.com/zetxek/adritian-demo)
|
||||
## triggers the update-hugo-module workflow in adritian-demo
|
||||
## and the update-git-submodule workflow in the git-submodule-demo site
|
||||
name: Update demo repos
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
pages: write
|
||||
pull-requests: write
|
||||
checks: write
|
||||
statuses: write
|
||||
actions: write
|
||||
|
||||
on:
|
||||
push:
|
||||
## run on push to main or a new semantic tag (ie: v1.2.3)
|
||||
branches:
|
||||
- main
|
||||
tags:
|
||||
- 'v[0-9]+.[0-9]+.[0-9]+'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
update:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
|
||||
- name: Trigger Update Hugo Module workflow
|
||||
run: |
|
||||
curl -X POST \
|
||||
-H "Authorization: token ${{ secrets.PRIVATE_TOKEN_GITHUB }}" \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
https://api.github.com/repos/zetxek/adritian-demo/actions/workflows/update-hugo-module.yml/dispatches \
|
||||
-d '{"ref":"main"}'
|
||||
|
||||
- name: Trigger Update Git Submodule workflow
|
||||
run: |
|
||||
curl -X POST \
|
||||
-H "Authorization: token ${{ secrets.PRIVATE_TOKEN_GITHUB }}" \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
https://github.com/zetxek/adritian-git-submodule-demo/actions/workflows/reset-submodule.yml/dispatches \
|
||||
-d '{"ref":"main"}'
|
||||
@@ -0,0 +1,147 @@
|
||||
## Updates demo site (https://github.com/zetxek/adritian-demo)
|
||||
## taken from https://stackoverflow.com/a/68213855/570087
|
||||
|
||||
name: PR demo site (on external PR)
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
pages: write
|
||||
pull-requests: write
|
||||
checks: write
|
||||
statuses: write
|
||||
actions: write
|
||||
|
||||
## This will open a PR which will open a vercel preview URL in the demo site
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [labeled]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
update-demo:
|
||||
env:
|
||||
SOURCE_BRANCH_NAME: ${{ github.head_ref || github.ref_name }} # PR branch name
|
||||
PR_NUMBER: ${{ github.event.number }}
|
||||
runs-on: ubuntu-latest
|
||||
if: contains(github.event.pull_request.labels.*.name, 'safe to test')
|
||||
steps:
|
||||
- name: Checkout source repository (theme)
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: theme-source
|
||||
ref: ${{ github.head_ref }}
|
||||
token: ${{ secrets.PRIVATE_TOKEN_GITHUB }}
|
||||
|
||||
- name: Checkout demo repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: zetxek/adritian-demo
|
||||
path: demo-repo
|
||||
token: ${{ secrets.PRIVATE_TOKEN_GITHUB }}
|
||||
|
||||
- name: Dump github context
|
||||
run: echo "$GITHUB_CONTEXT"
|
||||
shell: bash
|
||||
env:
|
||||
GITHUB_CONTEXT: ${{ toJson(github) }}
|
||||
|
||||
- name: Send pull-request
|
||||
run: |
|
||||
LATEST_TAG=$(cd theme-source && git describe --tags --always)
|
||||
LATEST_COMMIT=$(cd theme-source && git rev-parse HEAD)
|
||||
SOURCE_REPOSITORY="zetxek/adritian-free-hugo-theme"
|
||||
REPOSITORY="zetxek/adritian-demo"
|
||||
REPO_NAME=${{ github.event.pull_request.head.repo.full_name }}
|
||||
FOLDER="bin/$REPOSITORY"
|
||||
PR_URL="https://github.com/$SOURCE_REPOSITORY/pull/$PR_NUMBER"
|
||||
BRANCH_NAME="theme-update/update-theme-to-$LATEST_TAG"
|
||||
BASE_BRANCH="main"
|
||||
ASSIGNEE="zetxek"
|
||||
|
||||
echo "Latest tag: $LATEST_TAG"
|
||||
echo "Latest commit: $LATEST_COMMIT"
|
||||
echo "PR URL: $PR_URL"
|
||||
|
||||
git config --global --add --bool push.autoSetupRemote true
|
||||
|
||||
# Clone the remote repository and change working directory to the
|
||||
# folder it was cloned to.
|
||||
git clone \
|
||||
--depth=1 \
|
||||
--branch=main \
|
||||
https://some-user:${{ secrets.PRIVATE_TOKEN_GITHUB }}@github.com/$REPOSITORY \
|
||||
$FOLDER
|
||||
|
||||
cd $FOLDER
|
||||
|
||||
# Setup the committers identity.
|
||||
git config user.email "actions@github.com"
|
||||
git config user.name "GitHub Actions - update theme module version"
|
||||
|
||||
# Create a new feature branch for the changes.
|
||||
echo "Working branch: $BRANCH_NAME"
|
||||
git checkout -b $BRANCH_NAME
|
||||
|
||||
# Commit the changes and push the feature branch to origin
|
||||
git config --global --add --bool push.autoSetupRemote true
|
||||
echo "Committing all changes."
|
||||
git add --all
|
||||
|
||||
COMMIT_MSG_THEME='chore: update theme module version to `'"$LATEST_TAG"'`'
|
||||
echo 'Updating theme module: '"$COMMIT_MSG_THEME"
|
||||
git commit -am "$COMMIT_MSG_THEME" && git push --force || echo "No changes to theme"
|
||||
|
||||
# Copy content from the checked-out theme source
|
||||
cp ../../theme-source/exampleSite/hugo.toml hugo.toml
|
||||
# Update URL
|
||||
sed -i -e "s/\"https\:\/\/www\.adrianmoreno\.info\"/\"https\:\/\/adritian-demo\.vercel\.app\/\"/g" hugo.toml
|
||||
|
||||
COMMIT_MSG_CONFIG='chore: update config/content to `'"$LATEST_TAG"'` from https://github.com/zetxek/adritian-free-hugo-theme'
|
||||
echo "Committing content/config: $COMMIT_MSG_CONFIG"
|
||||
git commit -am "$COMMIT_MSG_CONFIG" && git push --force || echo "No changes to config"
|
||||
|
||||
echo "Pushing branch: $BRANCH_NAME"
|
||||
git push origin $BRANCH_NAME --force
|
||||
|
||||
# Store the PAT in a file that can be accessed by the
|
||||
# GitHub CLI.
|
||||
echo "${{ secrets.PRIVATE_TOKEN_GITHUB }}" > token.txt
|
||||
|
||||
# Authorize GitHub CLI for the current repository and
|
||||
# create a pull-requests containing the updates.
|
||||
echo "Logging in to GitHub CLI."
|
||||
gh auth login --with-token < token.txt
|
||||
|
||||
# Check if the PR already exists - if there's no "number" returned, we default to empty string
|
||||
PR_EXISTS=$(gh pr list --state open --base $BASE_BRANCH --head $BRANCH_NAME --json number | jq '.[0].number // empty')
|
||||
echo "PR_EXISTS: $PR_EXISTS"
|
||||
# Check if there's a PR number. If the PR exists, update it (empty = it doesn't exist)
|
||||
if [ -n "$PR_EXISTS" ]; then
|
||||
echo "PR Exists. Updating pull-request..."
|
||||
gh pr view
|
||||
|
||||
echo "✅ Pull-request created - done! "
|
||||
|
||||
# Else, we create it
|
||||
else
|
||||
echo "✨PR Does not exist yet. Creating pull-request..."
|
||||
PR_TITLE='preview: update theme to `'$SOURCE_BRANCH_NAME'`'
|
||||
echo 'PR title: '$PR_TITLE
|
||||
PR_BODY="⚠️ The source PR is not merged yet - this is a preview PR.
|
||||
🤖 This automated PR updates the theme version to a PR in the source repo: $PR_URL.
|
||||
🔗 Triggered by https://github.com/zetxek/adritian-free-hugo-theme/actions/workflows/update-demo-pr.yml"
|
||||
echo "PR body: "$PR_BODY
|
||||
|
||||
gh pr create \
|
||||
--title "$PR_TITLE" \
|
||||
--body "$PR_BODY" \
|
||||
--head $BRANCH_NAME \
|
||||
--base $BASE_BRANCH \
|
||||
--assignee $ASSIGNEE \
|
||||
--label preview
|
||||
|
||||
echo "✅ Pull-request created - done! "
|
||||
fi
|
||||
|
||||
|
||||
@@ -0,0 +1,260 @@
|
||||
## Updates demo site (https://github.com/zetxek/adritian-demo)
|
||||
## taken from https://stackoverflow.com/a/68213855/570087
|
||||
|
||||
name: Preview demo site
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
pages: write
|
||||
pull-requests: write
|
||||
checks: write
|
||||
statuses: write
|
||||
actions: write
|
||||
## This will open a PR which will open a vercel preview URL in the demo site
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [main]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
label-dependabot:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.actor == 'dependabot[bot]'
|
||||
steps:
|
||||
- name: Add safe-to-test label to Dependabot PRs
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
try {
|
||||
// First check if the label exists
|
||||
await github.rest.issues.getLabel({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
name: 'safe-to-test'
|
||||
});
|
||||
|
||||
// If it exists, add it to the PR
|
||||
await github.rest.issues.addLabels({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
labels: ['safe-to-test']
|
||||
});
|
||||
console.log('Added safe-to-test label to PR');
|
||||
} catch (error) {
|
||||
// If getting the label failed, the PR workflow can't add it
|
||||
// Let's just modify our workflow to handle this differently
|
||||
console.log('Could not add safe-to-test label: ' + error.message);
|
||||
console.log('Will handle Dependabot PRs directly in check-conditions job');
|
||||
}
|
||||
|
||||
check-dependabot:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
should-run: ${{ steps.check-conditions.outputs.should-run }}
|
||||
steps:
|
||||
- id: check-conditions
|
||||
run: |
|
||||
# Check if PR has safe-to-test label
|
||||
if [[ "${{ contains(github.event.pull_request.labels.*.name, 'safe-to-test') }}" == "true" ]]; then
|
||||
echo "should-run=true" >> $GITHUB_OUTPUT
|
||||
echo "PR has safe-to-test label, allowing workflow to run"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Always allow Dependabot PRs to run
|
||||
if [[ "${{ github.actor }}" == "dependabot[bot]" ]]; then
|
||||
echo "should-run=true" >> $GITHUB_OUTPUT
|
||||
echo "PR is from Dependabot, automatically allowing workflow to run"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# For other contributors
|
||||
echo "should-run=true" >> $GITHUB_OUTPUT
|
||||
echo "PR is from a trusted contributor, allowing workflow to run"
|
||||
|
||||
update-demo:
|
||||
needs: check-dependabot
|
||||
if: needs.check-dependabot.outputs.should-run == 'true'
|
||||
env:
|
||||
SOURCE_BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
|
||||
PR_NUMBER: ${{ github.event.number }}
|
||||
HUGO_VERSION: ${{ vars.HUGO_VERSION || '0.147.2' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout source repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: zetxek/adritian-free-hugo-theme
|
||||
ref: ${{ github.head_ref || github.ref_name }}
|
||||
token: ${{ secrets.PRIVATE_TOKEN_GITHUB }}
|
||||
path: source-repo
|
||||
|
||||
- name: Checkout demo repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: zetxek/adritian-demo
|
||||
token: ${{ secrets.PRIVATE_TOKEN_GITHUB }}
|
||||
path: demo-repo
|
||||
|
||||
- name: Setup Hugo
|
||||
uses: peaceiris/actions-hugo@v2
|
||||
with:
|
||||
hugo-version: ${{ env.HUGO_VERSION }}
|
||||
extended: true
|
||||
|
||||
- name: Send pull-request
|
||||
shell: bash
|
||||
run: |
|
||||
SOURCE_REPOSITORY="zetxek/adritian-free-hugo-theme"
|
||||
LATEST_COMMIT=${{ github.event.pull_request.head.sha || github.sha }}
|
||||
SHORT_COMMIT=${LATEST_COMMIT:0:8}
|
||||
|
||||
# Handle both PR events and manual workflow dispatch
|
||||
if [ -n "$PR_NUMBER" ]; then
|
||||
PR_URL="https://github.com/$SOURCE_REPOSITORY/pull/$PR_NUMBER"
|
||||
BRANCH_NAME="theme-update/pr-$PR_NUMBER-$SHORT_COMMIT"
|
||||
PR_TITLE="preview: theme PR #$PR_NUMBER ($SHORT_COMMIT)"
|
||||
else
|
||||
# For manual workflow dispatch, use branch name instead
|
||||
BRANCH_REF="${{ github.head_ref || github.ref_name }}"
|
||||
PR_URL="https://github.com/$SOURCE_REPOSITORY/tree/$BRANCH_REF"
|
||||
BRANCH_NAME="theme-update/branch-$BRANCH_REF-$SHORT_COMMIT"
|
||||
PR_TITLE="preview: theme branch $BRANCH_REF ($SHORT_COMMIT)"
|
||||
fi
|
||||
|
||||
BASE_BRANCH="main"
|
||||
ASSIGNEE="zetxek"
|
||||
|
||||
echo "Latest commit: $LATEST_COMMIT"
|
||||
echo "Short commit: $SHORT_COMMIT"
|
||||
echo "Branch name: $BRANCH_NAME"
|
||||
echo "PR URL: $PR_URL"
|
||||
if [ -n "$PR_NUMBER" ]; then
|
||||
echo "PR Number: $PR_NUMBER"
|
||||
else
|
||||
echo "Manual workflow dispatch - no PR number"
|
||||
fi
|
||||
|
||||
git config --global --add --bool push.autoSetupRemote true
|
||||
|
||||
# Verify source commit exists
|
||||
cd source-repo
|
||||
if ! git cat-file -e $LATEST_COMMIT; then
|
||||
echo "Error: Commit $LATEST_COMMIT does not exist in source repository"
|
||||
exit 1
|
||||
fi
|
||||
echo "✅ Verified commit $LATEST_COMMIT exists in source repository"
|
||||
|
||||
# Switch to demo repository
|
||||
cd ../demo-repo
|
||||
|
||||
# Setup git identity
|
||||
git config user.email "actions@github.com"
|
||||
git config user.name "GitHub Actions - update theme module"
|
||||
|
||||
# Create feature branch
|
||||
git checkout -b $BRANCH_NAME
|
||||
|
||||
# Initialize Hugo module if needed
|
||||
if [ ! -f "go.mod" ]; then
|
||||
hugo mod init github.com/zetxek/adritian-demo
|
||||
fi
|
||||
|
||||
# Clean up any existing invalid module references
|
||||
if [ -f "go.mod" ]; then
|
||||
echo "Cleaning up existing go.mod..."
|
||||
# Remove any existing theme references that might have invalid revisions
|
||||
grep -v 'github.com/zetxek/adritian-free-hugo-theme' go.mod > go.mod.tmp || touch go.mod.tmp
|
||||
mv go.mod.tmp go.mod
|
||||
# Clean up any existing go.sum entries
|
||||
if [ -f "go.sum" ]; then
|
||||
grep -v 'github.com/zetxek/adritian-free-hugo-theme' go.sum > go.sum.tmp || touch go.sum.tmp
|
||||
mv go.sum.tmp go.sum
|
||||
fi
|
||||
fi
|
||||
|
||||
# Get the theme from the PR branch - use branch name instead of commit for better reliability
|
||||
BRANCH_NAME_THEME="${{ github.head_ref || github.ref_name }}"
|
||||
echo "Using theme branch: $BRANCH_NAME_THEME"
|
||||
|
||||
# Try to get the specific commit first, fallback to branch if commit is not available
|
||||
echo "Attempting to get theme with commit: $LATEST_COMMIT"
|
||||
if hugo mod get github.com/zetxek/adritian-free-hugo-theme@$LATEST_COMMIT; then
|
||||
echo "✅ Successfully got theme with commit hash"
|
||||
else
|
||||
echo "Failed to get specific commit, trying branch instead..."
|
||||
echo "Attempting to get theme with branch: $BRANCH_NAME_THEME"
|
||||
if hugo mod get github.com/zetxek/adritian-free-hugo-theme@$BRANCH_NAME_THEME; then
|
||||
echo "✅ Successfully got theme with branch name"
|
||||
else
|
||||
echo "❌ Failed to get theme with both commit and branch, trying main branch as last resort..."
|
||||
hugo mod get github.com/zetxek/adritian-free-hugo-theme@main
|
||||
fi
|
||||
fi
|
||||
|
||||
hugo mod tidy
|
||||
|
||||
# Commit changes
|
||||
git add go.mod go.sum
|
||||
if [ -n "$PR_NUMBER" ]; then
|
||||
COMMIT_MSG="preview: theme PR #$PR_NUMBER ($SHORT_COMMIT)"
|
||||
else
|
||||
BRANCH_REF="${{ github.head_ref || github.ref_name }}"
|
||||
COMMIT_MSG="preview: theme branch $BRANCH_REF ($SHORT_COMMIT)"
|
||||
fi
|
||||
git commit -m "$COMMIT_MSG" && git push --force || echo "No changes to commit"
|
||||
|
||||
# Push branch
|
||||
git push origin $BRANCH_NAME --force
|
||||
|
||||
# Setup GitHub CLI
|
||||
echo "${{ secrets.PRIVATE_TOKEN_GITHUB }}" > token.txt
|
||||
gh auth login --with-token < token.txt
|
||||
|
||||
# Check if PR exists
|
||||
PR_EXISTS=$(gh pr list --state open --base $BASE_BRANCH --head $BRANCH_NAME --json number | jq '.[0].number // empty')
|
||||
echo "PR_EXISTS: $PR_EXISTS"
|
||||
|
||||
if [ -n "$PR_EXISTS" ]; then
|
||||
echo "PR Exists. Updating pull-request..."
|
||||
gh pr view
|
||||
echo "✅ Pull-request updated!"
|
||||
# TODO: update the PR body with the latest commit
|
||||
# Add a comment to the PR with the update
|
||||
gh pr comment $PR_EXISTS --body "Updated PR to last commit in source repo: \`$LATEST_COMMIT\`"
|
||||
else
|
||||
echo "✨ Creating new pull-request..."
|
||||
if [ -n "$PR_NUMBER" ]; then
|
||||
PR_BODY="⚠️ The source PR is not merged yet - this is a preview PR created to test the theme update with a vercel preview URL.
|
||||
- Last commit: \`$LATEST_COMMIT\`
|
||||
- Source PR: $PR_URL
|
||||
- 🔗 Triggered by https://github.com/zetxek/adritian-free-hugo-theme/actions/workflows/update-demo-pr.yml"
|
||||
else
|
||||
BRANCH_REF="${{ github.head_ref || github.ref_name }}"
|
||||
PR_BODY="⚠️ This is a preview PR created to test the theme update from branch \`$BRANCH_REF\` with a vercel preview URL.
|
||||
- Last commit: \`$LATEST_COMMIT\`
|
||||
- Source branch: $PR_URL
|
||||
- 🔗 Triggered by https://github.com/zetxek/adritian-free-hugo-theme/actions/workflows/update-demo-pr.yml"
|
||||
fi
|
||||
|
||||
PR_RESULT=$(gh pr create \
|
||||
--title "$PR_TITLE" \
|
||||
--body "$PR_BODY" \
|
||||
--head $BRANCH_NAME \
|
||||
--base $BASE_BRANCH \
|
||||
--assignee $ASSIGNEE \
|
||||
--label preview)
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error creating PR: $PR_RESULT"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "✅ Pull-request created!"
|
||||
fi
|
||||
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
name: Update Example Site
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
pages: write
|
||||
pull-requests: write
|
||||
checks: write
|
||||
statuses: write
|
||||
actions: write
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 0 * * 0' # Every Sunday at midnight
|
||||
|
||||
jobs:
|
||||
update-example-site:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
DATE: ${{ format('YYYY-MM-DD', github.event.repository.pushed_at) }}
|
||||
|
||||
steps:
|
||||
- name: Set date
|
||||
run: echo "DATE=$(date +'%Y-%m-%d')" >> $GITHUB_ENV
|
||||
|
||||
- name: Set branch name
|
||||
run: echo "BRANCH_NAME=update/exampleSite-${DATE}" >> $GITHUB_ENV
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Install Node.js dependencies
|
||||
run: npm i
|
||||
|
||||
- name: Update example site content
|
||||
run: |
|
||||
cd exampleSite
|
||||
../node_modules/@zetxek/adritian-theme-helper/dist/scripts/download-content.js
|
||||
|
||||
- name: Create new branch
|
||||
run: |
|
||||
git checkout -b ${{ env.BRANCH_NAME }}
|
||||
|
||||
- name: Commit changes
|
||||
run: |
|
||||
git config --global user.name 'GitHub Actions'
|
||||
git config --global user.email 'actions@github.com'
|
||||
git add .
|
||||
git commit -m "Update example site content"
|
||||
|
||||
- name: Push changes
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
git push origin ${{ env.BRANCH_NAME }}
|
||||
|
||||
- name: Create Pull Request
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
PR_TITLE="ExampleSite content update (${DATE})"
|
||||
PR_BODY="This automated PR updates the example site with the latest content from [adritian-demo](https://github.com/zetxek/adritian-demo).
|
||||
|
||||
**Overview of changes:**
|
||||
|
||||
<code>
|
||||
$(git diff --stat HEAD~1)
|
||||
</code>"
|
||||
gh pr create --title "$PR_TITLE" --body "$PR_BODY" --base main --head "${{ env.BRANCH_NAME }}"
|
||||
@@ -0,0 +1,25 @@
|
||||
Thumbs.db
|
||||
.DS_Store
|
||||
.dist
|
||||
.tmp
|
||||
.sass-cache
|
||||
npm-debug.log
|
||||
node_modules
|
||||
builds
|
||||
|
||||
# Playwright
|
||||
/test-results/
|
||||
/tests/coverage-report
|
||||
playwright-report
|
||||
test-results
|
||||
exampleSite/public
|
||||
exampleSite/resources
|
||||
exampleSite/.hugo_build.lock
|
||||
exampleSite/hugo_stats.json
|
||||
|
||||
# Local builds
|
||||
.hugo_build.lock
|
||||
public/
|
||||
|
||||
# idea
|
||||
.idea/
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"plugins": [
|
||||
"prettier-plugin-go-template"
|
||||
],
|
||||
"overrides": [
|
||||
{
|
||||
"files": [
|
||||
"*.html"
|
||||
],
|
||||
"options": {
|
||||
"parser": "go-template"
|
||||
}
|
||||
}
|
||||
],
|
||||
"htmlWhitespaceSensitivity": "ignore",
|
||||
"bracketSameLine": true,
|
||||
"goTemplateBracketSpacing": true
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"esbenp.prettier-vscode"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
Adrián Moreno Peña, zetxek@gmail.com.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series
|
||||
of actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or
|
||||
permanent ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within
|
||||
the community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.0, available at
|
||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||
|
||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||
enforcement ladder](https://github.com/mozilla/diversity).
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
https://www.contributor-covenant.org/faq. Translations are available at
|
||||
https://www.contributor-covenant.org/translations.
|
||||
@@ -0,0 +1,54 @@
|
||||
# Contribution Guidelines
|
||||
|
||||
Thank you for considering contributing to our project! Contributions are very welcome 🙏
|
||||
|
||||
We appreciate your time and effort in helping us improve the project. To ensure a smooth collaboration, please follow these guidelines:
|
||||
|
||||
## Getting Started
|
||||
|
||||
1. **Fork the Repository**: Start by forking the repository to your GitHub account.
|
||||
2. **Clone the Repository**: Clone the forked repository to your local machine using:
|
||||
```bash
|
||||
git clone https://github.com/zetxek/adritian-free-hugo-theme.git
|
||||
```
|
||||
3. **Create a Branch**: Create a new branch for your feature or bug fix:
|
||||
```bash
|
||||
git checkout -b feature/your-feature-name
|
||||
```
|
||||
|
||||
## Making Changes
|
||||
|
||||
1. **Code Style**: Follow the project's coding style and conventions. Ensure your code is clean and well-documented.
|
||||
2. **Commit Messages**: Write clear and concise commit messages. Use the present tense and include a brief description of the changes.
|
||||
3. **Testing**: Ensure your changes work well with the test site (and others, maybe with yours), in mobile and desktop browsers.
|
||||
|
||||
## Submitting Changes
|
||||
|
||||
1. **Push Changes**: Push your changes to your forked repository:
|
||||
```bash
|
||||
git push origin feature/your-feature-name
|
||||
```
|
||||
2. **Create a Pull Request**: Open a pull request (PR) from your branch to this repository's `main` branch. Provide a detailed description of your changes and link any relevant issues.
|
||||
|
||||
## Code Review
|
||||
|
||||
1. **Review Process**: Your PR will be reviewed by project maintainers (mainly @zetxek). Be responsive to feedback and make necessary changes.
|
||||
2. **Approval**: Once your PR is approved, it will be merged into the main branch.
|
||||
|
||||
🎗️ Remember that the maintainers are working in a voluntary basis - do not expect a quick turnaround, as they will get to the issue when life allows.
|
||||
|
||||
|
||||
## Community Guidelines
|
||||
|
||||
You can read about our guidelines in our [code of conduct](https://github.com/zetxek/adritian-free-hugo-theme/blob/main/CODE_OF_CONDUCT.md).
|
||||
|
||||
## Reporting Issues
|
||||
|
||||
1. **Bug Reports**: If you find a bug, please open an [issue](https://github.com/zetxek/adritian-free-hugo-theme/issues) with a clear description and steps to reproduce the problem.
|
||||
2. **Feature Requests**: If you have an idea for a new feature, open an [issue](https://github.com/zetxek/adritian-free-hugo-theme/issues) to discuss it with the community.
|
||||
|
||||
Support is not offered in private, direct mails - as they won't directly benefit the community.
|
||||
|
||||
## License
|
||||
|
||||
By contributing to this project, you agree that your contributions will be licensed under the project's [LICENSE](LICENSE) file.
|
||||
@@ -0,0 +1,130 @@
|
||||
# New Features Documentation
|
||||
|
||||
This document describes the new features added to the Adritian Hugo theme to make it more competitive with other popular Hugo themes.
|
||||
|
||||
## Features Overview
|
||||
|
||||
### 1. Related Posts
|
||||
|
||||
**What it does:** Automatically displays related blog posts at the end of each post based on shared tags and publication dates.
|
||||
|
||||
**How to use:** No configuration needed - it works automatically for all blog posts.
|
||||
|
||||
**Customization:** The related content algorithm can be adjusted in `hugo.toml` under the `[related]` section.
|
||||
|
||||
### 2. Social Sharing Buttons
|
||||
|
||||
**What it does:** Adds sharing buttons for Twitter/X, LinkedIn, Facebook, and Email to all blog posts.
|
||||
|
||||
**How to use:** Enabled by default. To customize:
|
||||
|
||||
```toml
|
||||
[params.sharing]
|
||||
enabled = true # Set to false to disable
|
||||
twitter = true # Show/hide individual platforms
|
||||
linkedin = true
|
||||
facebook = true
|
||||
email = true
|
||||
```
|
||||
|
||||
### 3. Table of Contents (TOC)
|
||||
|
||||
**What it does:** Auto-generates a table of contents from post headings for easy navigation.
|
||||
|
||||
**How to use:** Add to post frontmatter:
|
||||
|
||||
```yaml
|
||||
---
|
||||
toc: true
|
||||
tocSticky: true # Optional: makes TOC sticky on desktop
|
||||
---
|
||||
```
|
||||
|
||||
**Requirements:** Only appears if post has more than 400 words.
|
||||
|
||||
### 4. Comments Integration
|
||||
|
||||
**What it does:** Supports three popular comment systems: Disqus, Giscus, and Utterances.
|
||||
|
||||
**How to use:**
|
||||
|
||||
#### Option 1: Disqus
|
||||
```toml
|
||||
disqusShortname = "your-shortname"
|
||||
|
||||
[params.comments]
|
||||
enabled = true
|
||||
provider = "disqus"
|
||||
```
|
||||
|
||||
#### Option 2: Giscus (GitHub Discussions)
|
||||
```toml
|
||||
[params.comments]
|
||||
enabled = true
|
||||
provider = "giscus"
|
||||
|
||||
[params.comments.giscus]
|
||||
repo = "username/repo"
|
||||
repoId = "R_xxxxx"
|
||||
category = "General"
|
||||
categoryId = "DIC_xxxxx"
|
||||
```
|
||||
|
||||
#### Option 3: Utterances (GitHub Issues)
|
||||
```toml
|
||||
[params.comments]
|
||||
enabled = true
|
||||
provider = "utterances"
|
||||
|
||||
[params.comments.utterances]
|
||||
repo = "username/repo"
|
||||
issueTerm = "pathname"
|
||||
theme = "preferred-color-scheme"
|
||||
```
|
||||
|
||||
### 5. Enhanced Reading Metadata
|
||||
|
||||
**What it does:** Displays additional metadata for blog posts:
|
||||
- Estimated reading time
|
||||
- Last modified date (if specified)
|
||||
- Word count
|
||||
|
||||
**How to use:** Automatic for reading time and word count. For last modified:
|
||||
|
||||
```yaml
|
||||
---
|
||||
title: "My Post"
|
||||
date: 2025-01-15
|
||||
lastmod: 2025-02-20 # Add this field
|
||||
---
|
||||
```
|
||||
|
||||
## Styling
|
||||
|
||||
All new features include:
|
||||
- Full dark mode support
|
||||
- Responsive design for mobile/tablet/desktop
|
||||
- Consistent styling with the theme
|
||||
- Accessibility features (ARIA labels, semantic HTML)
|
||||
|
||||
## Translations
|
||||
|
||||
All features support the theme's multilingual capabilities with translations for:
|
||||
- English (en)
|
||||
- Spanish (es)
|
||||
- French (fr)
|
||||
- German (de)
|
||||
- Dutch (nl)
|
||||
- Danish (da)
|
||||
- Italian (it)
|
||||
- Portuguese (pt)
|
||||
- Swedish (sv)
|
||||
- Norwegian (no)
|
||||
- Polish (pl)
|
||||
|
||||
Additional languages can be added by creating translation files in `i18n/`.
|
||||
|
||||
## Demo
|
||||
|
||||
See all features in action at the demo blog post:
|
||||
`/blog/new-features-demo/` in the example site.
|
||||
@@ -0,0 +1,20 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright for portions of the theme are held by (c) 2020 Radity (https://radity.com). Other copyright is held by Adrián Moreno Peña, 2022.
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,385 @@
|
||||
# Adritian Free Hugo Theme
|
||||
A modern, fast and extensible Hugo theme for personal websites and professional landing pages - with blog and portfolio support
|
||||
|
||||
[
|
||||
](https://adritian-demo.vercel.app/) [](https://github.com/zetxek/adritian-free-hugo-theme/actions/workflows/test-example-site.yml)
|
||||
|
||||
## 🚀 Key Features
|
||||
|
||||
- 🏎️ Fast, minimalistic code (no jQuery or other javascript frameworks)
|
||||
- 🖼️ Bootstrap v5 (`v5.3.7`) CSS framework with Scss customization
|
||||
- 📚 Multi-language (i18n) support
|
||||
- 🛠️ Custom content types (work experience, education, projects/work showcase, testimonials, blog)
|
||||
- 🧰 Multiple shortcodes to customize your landing page in any way you want
|
||||
- 🔎 Lightning fast search (powered by [fuse.js](https://www.fusejs.io/))
|
||||
- 💯 Perfect Lighthouse scores (Performance, Accessibility, SEO)
|
||||
- 🌚 Automatic dark/light theme switching, with manual override
|
||||
- 🖨️ Print-friendly CV template
|
||||
- 🔧 Technical Skills showcase with visual skill bars and categories
|
||||
- ⚡ Vercel-ready with Analytics & Speed Insights support
|
||||
- 🖼️ Menus with icon support
|
||||
- 🔗 **Related posts** - Automatically display related content based on tags
|
||||
- 📤 **Social sharing buttons** - Built-in sharing for Twitter/X, LinkedIn, Facebook, and Email
|
||||
- 📑 **Table of Contents** - Auto-generated, sticky TOC for blog posts
|
||||
- 💬 **Comments integration** - Support for Disqus, Giscus, and Utterances
|
||||
- ⏱️ **Reading time display** - Estimated reading time for blog posts
|
||||
- 🏷️ **Enhanced taxonomy support** - Improved tags and categories display
|
||||
|
||||
The theme focuses on accessibility, high performance, and usability (it's very easy to get started). It's extendable by adding your own styles or content types, and it has a solid foundation on which to build.
|
||||
|
||||
Some of the best applications for the theme are for minimalistic websites, single-page applications, and personal portfolios. It has a contact form you can customize to your mail address without setting up a backend.
|
||||
|
||||
> [!TIP]
|
||||
> Do you like the theme? 🌟 Star the repo on GitHub to help make it more visible
|
||||
|
||||
## Live demo & Preview
|
||||
|
||||
You can see it live at [www.adrianmoreno.info](https://www.adrianmoreno.info) (my personal website), as well as in these screenshots of the homepage, in the dark and light variations of the theme:
|
||||
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<img src="https://user-images.githubusercontent.com/240085/230632835-74349170-d610-4731-8fac-62c413e6b3f5.png" alt="Light version of the Hugo theme Adritian"/>
|
||||
</td>
|
||||
<td>
|
||||
<img src="https://raw.githubusercontent.com/zetxek/adritian-free-hugo-theme/main/images/screenshot-dark-fullscroll.jpeg" alt="Dark version of the Hugo theme Adritian"/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
The dark color variation is selected automatically based on browser settings, and a color switcher is available in the footer and the mobile menu for visitors to override.
|
||||
|
||||
Other relevant repositories related to this theme are:
|
||||
|
||||
1. [adritian-theme-helper](https://github.com/zetxek/adritian-theme-helper): npm package helper, used to bootstrap the theme. It helps initialize a site with the right content and configuration files.
|
||||
1. A full-featured site, [my personal website](https://www.adrianmoreno.info) [in github too](https://github.com/zetxek/adrianmoreno.info), so you can see how the theme can look real-life (including deployment scripts).
|
||||
1. A simpler [demo site for the theme, adritian-demo](https://adritian-demo.vercel.app/) ([and its code](https://github.com/zetxek/adritian-demo)), where the demo content comes from.
|
||||
1. The same demo site, in a [git submodules integration](https://github.com/zetxek/adritian-git-submodule-demo).
|
||||
|
||||
💡 For more inspiration, check this document's [showcase section](#showcase).
|
||||
|
||||
✨ This theme is entirely free and open source. We welcome your ideas, feedback, and contributions! If you find it useful, please give it a GitHub star to show your support.
|
||||
|
||||
## Quickstart
|
||||
|
||||
#### Install Hugo
|
||||
|
||||
This is a theme for the website generator Hugo. To use it, you must install Hugo by following [the official guide](https://gohugo.io/getting-started/installing/).
|
||||
|
||||
**We recommend installing the theme as a [Hugo module](https://gohugo.io/hugo-modules/) (recommended, and explained below).** This is the most powerful way to install the theme, will allow you to combine it with other modules, and is probably the easiest way to update across theme versions.
|
||||
|
||||
Another alternative is to use [git submodules](https://gohugo.io/getting-started/quick-start/#create-a-site) or to [download the theme as a zip file](https://github.com/zetxek/adritian-free-hugo-theme/releases) and copy the files to your site`*`. But that will make your site "stuck in time" and more difficult to upgrade. **This is not recommended or supported directly**.
|
||||
|
||||
> **On the release files:** `*` from the version `v1.5.4` the theme available as a zip file in the [releases page](https://github.com/zetxek/adritian-free-hugo-theme/releases) contains the `node_modules` folder, so you don't need to install it separately. This is a convenience for edge cases that might have problems installing the theme as a module or downloading many files.__
|
||||
|
||||
### As a Hugo Module (recommended)
|
||||
|
||||
> **Note:** Before proceeding, **Ensure you have Go and Hugo installed** and that you have created a new Hugo project.
|
||||
As a pre-requirement, you will need Hugo set up and running. You can follow [the official guide for it](https://gohugo.io/categories/installation/).
|
||||
|
||||
The theme has been tested with Hugo version `0.136` (extended version). If you get errors regarding missing functionalities, check if you have the latest version of Hugo available.
|
||||
|
||||
**Note:** as mentioned, the theme supports both Hugo modules and git submodules. You should use Hugo modules to install the theme in the most maintainable way. If you prefer git submodules you can follow these [older instructions](https://gohugobrasil.netlify.app/themes/installing-and-using-themes/) or the next ones as help:
|
||||
|
||||
<details>
|
||||
<summary>Step-by-step instructions to setup the theme as a hugo module</summary>
|
||||
|
||||
1. Create a new Hugo site (this will create a new folder): `hugo new site <your website's name>`
|
||||
1. Enter the newly created folder: `cd <your website's name>/`
|
||||
1. Initialize the Hugo Module system in your site if you haven't already: `hugo mod init github.com/username/your-site` (_you don't need to host your website on GitHub, you can add anything as a name_)
|
||||
1. Install the theme as a module: `hugo mod get -u github.com/zetxek/adritian-free-hugo-theme`
|
||||
1. Add the following to your `hugo.toml`, to set the theme as active:
|
||||
|
||||
```
|
||||
[module]
|
||||
[[module.imports]]
|
||||
path = "github.com/zetxek/adritian-free-hugo-theme"
|
||||
```
|
||||
|
||||
6. Prepare the `package.json` file: `hugo mod npm pack`
|
||||
7. Install the dependencies: `npm install`. This will include Bootstrap (needed for styling) and the helper script [adritian-theme-helper](https://github.com/zetxek/adritian-theme-helper).
|
||||
8. Run the initial content downloader: `./node_modules/@zetxek/adritian-theme-helper/dist/scripts/download-content.js`. This will download the demo content from the [adritian-demo](https://github.com/zetxek/adritian-demo) repository and copy it to your site, for a quick start (including translations, images, configuration and content)
|
||||
</details>
|
||||
|
||||
|
||||
### Running the site
|
||||
|
||||
The initial configuration allows you to have a base to edit and adapt to your site, and see the available functionalities.
|
||||
**Make sure to edit `baseURL`, `title` and `description`**. You can edit the header links, as well as the languages to your needs.
|
||||
|
||||
After you have installed the `npm` packages and setup the initial contents, you can
|
||||
|
||||
1. Start Hugo with `hugo server`...
|
||||
1. 🎉 The theme is alive on http://localhost:1313/
|
||||
|
||||
For next steps and guidance on where to customize your content, [check the demo site](https://adritian-demo.vercel.app/).
|
||||
You can check the demo site help page for other installation methods (such as submodules or manual configuration).
|
||||
|
||||
### Additional features and configuration
|
||||
|
||||
The theme is extensible and customizable in multiple areas, and it can be tricky to figure out exactly what to edit. This is a guide (that is complemented by the [demo site](https://adritian-demo.vercel.app/)).
|
||||
|
||||
<img width="1395" alt="image" src="https://github.com/user-attachments/assets/270c4445-5354-441a-ab23-21d91762e33c" />
|
||||
|
||||
#### Multi-language support
|
||||
|
||||
https://github.com/user-attachments/assets/030e765a-275f-4141-88e0-b854ebe551da
|
||||
|
||||
The theme implements the [internationalization (i18n) system by Hugo](https://gohugo.io/content-management/multilingual/), to enable multilingual sites.
|
||||
|
||||
See the content in the `i18n` folder to edit the translations, and the configuration `hugo.toml` to define your active languages. The theme includes translations for:
|
||||
|
||||
- **English** (`en`)
|
||||
- **Spanish** (`es`)
|
||||
- **French** (`fr`)
|
||||
- **German** (`de`)
|
||||
- **Dutch** (`nl`)
|
||||
- **Danish** (`da`)
|
||||
- **Italian** (`it`)
|
||||
- **Portuguese** (`pt`)
|
||||
- **Swedish** (`sv`)
|
||||
- **Norwegian** (`no`)
|
||||
- **Polish** (`pl`)
|
||||
|
||||
The example site has 3 enabled languages by default (`en`, `es`, and `fr`). You can enable additional languages by adding them to your `hugo.toml` configuration, or disable the provided ones (by setting `disabled` to `true` on the languages you don't need).
|
||||
|
||||
Most of the content is expected to be translated via the content system of Hugo:
|
||||
|
||||
- [by file name](https://gohugo.io/content-management/multilingual/#translation-by-file-name)
|
||||
- [by content directory](https://gohugo.io/content-management/multilingual/#translation-by-content-directory)
|
||||
|
||||
Note: The introduction of i18n support was done in the version `v1.3.0` and it has breaking changes due to the way in which the content was managed. You can read about the upgrade path in [UPGRADING.md](UPGRADING.md). In the version `v1.7.0` the usage of Hugo's content management was expanded, to use less `i18n` strings and more file name/content directory based translations.
|
||||
|
||||
#### Editing the theme content
|
||||
|
||||
You can check the repository [adritian-demo](https://github.com/zetxek/adritian-demo) for a reference implementation, as well as the [theme website](https://adritian-demo.vercel.app/) (https://adritian-demo.vercel.app/), to get a visual guide on how to edit the content.
|
||||
|
||||
Following the initial setup instructions you will get a "ready-to-use" version of the site, with sample content for you to edit and customize.
|
||||
|
||||
#### Technical Skills Showcase
|
||||
|
||||
The theme includes a dedicated Technical Skills showcase section that allows you to visually display your skills with progress bars and categorization:
|
||||
|
||||
- Create a `/skills` section in your site with customizable skill categories
|
||||
- Each skill can include name, proficiency level, years of experience, and description
|
||||
- Visual skill bars automatically use your theme's primary color
|
||||
- Full dark mode support with appropriate contrast
|
||||
- Responsive design for all device sizes
|
||||
- Accessible through navigation menu with multilingual support
|
||||
|
||||
To use this feature, create a `skills/_index.md` file with structured front matter for your skill categories and individual skills. The theme will automatically generate a visually appealing skills showcase page.
|
||||
|
||||
#### Shortcodes
|
||||
|
||||
The theme has multiple shortcodes available for use in the content, so you can customize your homepage (or any other page) as you want. You can read about them in the [shortcodes page](https://adritian-demo.vercel.app/blog/shortcodes). Since version `v1.7.0,` this is the preferred way to set up your theme content and translations, as that's the most flexible system.
|
||||
|
||||
You can see the shortcodes in use in the demo site's pages, like
|
||||
|
||||
- [home page](https://adritian-demo.vercel.app/) [`(source)`](https://raw.githubusercontent.com/zetxek/adritian-demo/refs/heads/main/content/home/home.md)
|
||||
- [CV page](https://adritian-demo.vercel.app/cv) [`(source)`](https://raw.githubusercontent.com/zetxek/adritian-demo/refs/heads/main/content/cv.md)
|
||||
|
||||
#### Contact form
|
||||
_(optional, if you want to use the contact form)_ edit the key `contact` in your `homepage.yml` file, to customize your mail address. Sign up in [formspree](https://formspree.io) to redirect mails to your own.
|
||||
|
||||
It can be rendered in any page via the `contact-section` shortcode.
|
||||
|
||||
#### Blog
|
||||
|
||||
Two layouts are available for the blog:
|
||||
- `default` (full-width for posts)
|
||||
- `sidebar` (sidebar with recent posts and categories)
|
||||
|
||||
| Default Layout | Sidebar Layout |
|
||||
|---------------|----------------|
|
||||
|  |  |
|
||||
| Full width posts | Posts with left sidebar |
|
||||
| Clean, focused reading experience | Shows recent posts and categories |
|
||||
| Maximizes content area | 25% width sidebar by default |
|
||||
| Best for image-heavy posts | Helps with site navigation |
|
||||
|
||||
<img width="1271" alt="image" src="https://github.com/user-attachments/assets/1821a3b7-f572-4958-8c4f-bd1687cc8f71">
|
||||
|
||||
|
||||
To use the blog, you can use the content type "blog", and render it in the URL `/blog`.
|
||||
You can add a menu link to it in `hugo.toml`.
|
||||
|
||||
The posts will be markdown files stored in the `content/blog` folder.
|
||||
|
||||
The layout can be configured in the `hugo.toml` file, under the `[params.blog]` section:
|
||||
|
||||
```toml
|
||||
[params.blog]
|
||||
layout = "sidebar-right" # options: default (no sidebar), sidebar, sidebar-right
|
||||
sidebarWidth = "25" # percentage width of the sidebar (when using sidebar layouts)
|
||||
narrowContent = true # if true, limits content width to 720px for better readability (default: true)
|
||||
showCategories = true # show categories in the sidebar
|
||||
showRecentPosts = true # show recent posts in the sidebar
|
||||
recentPostCount = 5 # number of recent posts to display
|
||||
listStyle = "summary" # options: simple, summary
|
||||
```
|
||||
|
||||
**Configuration options:**
|
||||
- `layout`: Choose between `default` (no sidebar), `sidebar` (left sidebar), or `sidebar-right` (right sidebar)
|
||||
- `sidebarWidth`: Set the percentage width of the sidebar (e.g., "25" for 25%)
|
||||
- `narrowContent`: When set to `true` (default), limits the blog content width to 720px for optimal readability. Set to `false` for full-width content
|
||||
- `showCategories`: Display category links in the sidebar
|
||||
- `showRecentPosts`: Display recent posts in the sidebar
|
||||
- `recentPostCount`: Number of recent posts to show in the sidebar
|
||||
- `listStyle`: Choose between `simple` (title and date only) or `summary` (includes excerpt)
|
||||
|
||||
##### Blog Post Features
|
||||
|
||||
**Related Posts**
|
||||
|
||||
The theme automatically shows related posts at the end of each blog post based on shared tags and publish dates. This is powered by Hugo's built-in related content feature.
|
||||
|
||||
**Table of Contents**
|
||||
|
||||
Enable a table of contents for any blog post by adding these parameters to your post's frontmatter:
|
||||
|
||||
```yaml
|
||||
---
|
||||
toc: true
|
||||
tocSticky: true # optional, makes TOC sticky on desktop
|
||||
---
|
||||
```
|
||||
|
||||
The TOC automatically generates from your post headings and only appears when the post has more than 400 words.
|
||||
|
||||
**Social Sharing**
|
||||
|
||||
Social sharing buttons are enabled by default for all blog posts. You can customize which platforms appear in your `hugo.toml`:
|
||||
|
||||
```toml
|
||||
[params.sharing]
|
||||
enabled = true
|
||||
twitter = true
|
||||
linkedin = true
|
||||
facebook = true
|
||||
email = true
|
||||
```
|
||||
|
||||
**Comments**
|
||||
|
||||
The theme supports popular comment systems. Configure in your `hugo.toml`:
|
||||
|
||||
```toml
|
||||
[params.comments]
|
||||
enabled = true
|
||||
provider = "giscus" # options: "disqus", "giscus", "utterances"
|
||||
|
||||
# For Giscus (GitHub Discussions)
|
||||
[params.comments.giscus]
|
||||
repo = "username/repo"
|
||||
repoId = "your-repo-id"
|
||||
category = "General"
|
||||
categoryId = "your-category-id"
|
||||
|
||||
# For Utterances (GitHub Issues)
|
||||
[params.comments.utterances]
|
||||
repo = "username/repo"
|
||||
issueTerm = "pathname"
|
||||
theme = "preferred-color-scheme"
|
||||
```
|
||||
|
||||
For Disqus, set your `disqusShortname` in the site configuration.
|
||||
|
||||
**Reading Time & Metadata**
|
||||
|
||||
Blog posts automatically display:
|
||||
- Estimated reading time
|
||||
- Word count
|
||||
- Publication date
|
||||
- Last modified date (if `lastmod` is set in frontmatter)
|
||||
|
||||
#### (Job) Experience
|
||||
|
||||
This functionality and content is especially suited for personal professional sites, showcasing the work experience:
|
||||
|
||||
<img width="1444" alt="SCR-20240624-uaoi" src="https://github.com/zetxek/adritian-free-hugo-theme/assets/240085/9ea86d6a-62c6-4c4f-96ba-8450fa24dd68">
|
||||
|
||||
It can be used to render job experience, projects and/or clients. Each experience/project has a duration, job title, company name, location and description/excerpt as well as a longer text.
|
||||
|
||||
The experience is managed through a specific content type (see `content/experience` for an example).
|
||||
You can use `hugo new experience/experience-name.md` (replacing `experience-name` by the name of the job experience).
|
||||
This will create the content in the `content/experience` folder, following the `experience` archetype.
|
||||
|
||||
The following fields are used from the file's Front Matter: `title`, `jobTitle`, `company`, `location`, `duration`.
|
||||
You can find a sample experience file content here:
|
||||
|
||||
```
|
||||
---
|
||||
date: 2007-12-01T00:00:00+01:00
|
||||
draft: false
|
||||
title: "Job #1"
|
||||
jobTitle: "Junior Intern"
|
||||
company: "Internet Affairs Inc. "
|
||||
location: "Stavanger, Norway"
|
||||
duration: "2022-2024"
|
||||
|
||||
---
|
||||
### Fixing the world, one byte at a time
|
||||
|
||||
The beginning of a great career.
|
||||
```
|
||||
|
||||
The experience can be displayed in several locations and different styles:
|
||||
|
||||
| Shortcode usage | Experience page | Single experience item | Print-friendly list |
|
||||
|---------------|----------------|----------------|----------------|
|
||||
|  |  |  |  |
|
||||
| Experience list rendered via shortcode | Experience list in /experience | Experience item in in single page | Full-width print-friendly experience
|
||||
| [🔗](https://adritian-demo.vercel.app/#experience-single) By using the shortcode `experience-section`: in a page (such as your homepage), with a limited number of experiences (controlled by the config parameter `homepageExperienceCount` in the file `hugo.toml`). The summary is displayed, as well as an introduction text and optional links | [🔗](https://adritian-demo.vercel.app/experience) Automatically, in the **Experience page**, in `/experience`, with a list of all experiences (no limit). The summary is displayed for each item, as well as a introduction text and optional links. | [🔗](https://adritian-demo.vercel.app/experience/job-1/) Individual experience page (such as `/experience/job-1`), where all details are displayed, and links to the other descriptions are available to navigate. | [🔗](https://adritian-demo.vercel.app/cv) Using the shortcode `experience-list` you can generate a list of experience that is not interactive - good for print-friendly layouts.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
This theme is a version of the one found on my website [adrianmoreno.info](https://www.adrianmoreno.info). If you run into trouble, [you can check the code on my website](https://github.com/zetxek/adrianmoreno.info) for reference.
|
||||
|
||||
If you have improvements for the theme, you are very welcome to make a PR if you are able 💕. Otherwise - see below for how to get help (and maybe help others with the same problem).
|
||||
|
||||
## Testing
|
||||
|
||||
The theme includes comprehensive E2E tests using Playwright to ensure all features work correctly.
|
||||
|
||||
### Running Tests
|
||||
|
||||
```bash
|
||||
# Run all tests
|
||||
npm test
|
||||
|
||||
# Run E2E tests only
|
||||
npm run test:e2e
|
||||
|
||||
# Run new features tests specifically
|
||||
npm run test:e2e:new-features
|
||||
|
||||
# Run tests in UI mode (interactive)
|
||||
npm run test:e2e:ui
|
||||
```
|
||||
|
||||
### Test Coverage
|
||||
|
||||
The test suite includes **27 comprehensive tests** for the new blog features:
|
||||
- Related Posts functionality
|
||||
- Social Sharing buttons (Twitter, LinkedIn, Facebook, Email)
|
||||
- Table of Contents generation and navigation
|
||||
- Enhanced reading metadata (reading time, word count, dates)
|
||||
- Dark mode compatibility
|
||||
- Responsive design
|
||||
- Accessibility features
|
||||
- Multilingual support
|
||||
- Performance validation
|
||||
|
||||
See `tests/README.md` for detailed testing documentation.
|
||||
|
||||
### Common issues
|
||||
|
||||
- The site fails to build. Look for the last line of the stacktrace - if you find mentions to missing files, such as in
|
||||
```
|
||||
Error: error building site: TOCSS: failed to transform "/scss/adritian.scss" (text/x-scss): ".../.cache/hugo_cache/modules/filecache/modules/pkg/mod/github.com/zetxek/adritian-free-hugo-theme@v1.5.6/assets/scss/adritian.scss:1:1": File to import not found or unreadable: bootstrap/bootstrap.
|
||||
```
|
||||
Make sure that you have the dependencies installed. Check the troubleshooting steps in the [following issue](https://github.com/zetxek/adritian-free-hugo-theme/issues/194#issuecomment-2634193132).
|
||||
|
||||
- The site renders in a weird-looking way, or you miss content. Check that the co
|
||||
@@ -0,0 +1,465 @@
|
||||
# Upgrading across versions
|
||||
|
||||
This documentation is meant to help you upgrade across versions, when potentially breaking changes are introduced.
|
||||
|
||||
## v1.7.19
|
||||
|
||||
Adding a new page transition effect.
|
||||
The new effect is applied to all pages, and is triggered when navigating through the site. It can be disabled in the `config.toml` file, under the `params.pageTransition` section.
|
||||
|
||||
By default, the effect is enabled.
|
||||
|
||||
## v1.7.12
|
||||
|
||||
__Introducing 🔎Search__
|
||||
To be able to use the search functionality, you need to:
|
||||
|
||||
1. add a new `module.mount` in your `hugo.toml` file (in the same place where you reference bootstrap files):
|
||||
|
||||
```
|
||||
## Search & sanitization
|
||||
[[module.mounts]]
|
||||
source = "node_modules/fuse.js/dist/fuse.min.js"
|
||||
target = "static/js/fuse.min.js"
|
||||
[[module.mounts]]
|
||||
source = "node_modules/dompurify/dist/purify.min.js"
|
||||
target = "static/js/purify.min.js"
|
||||
```
|
||||
|
||||
2. add the following to your `hugo.toml` file:
|
||||
|
||||
```
|
||||
home = ["HTML", "RSS", "JSON"]
|
||||
```
|
||||
Under the `outputs` area.
|
||||
|
||||
3. add a new page to your site, `content/search.md`:
|
||||
|
||||
```
|
||||
---
|
||||
title: "Search Results"
|
||||
sitemap:
|
||||
priority : 0.1
|
||||
layout: "search"
|
||||
---
|
||||
```
|
||||
|
||||
You can now test the search by visiting `/search` in your site. It should load a page [similar to what is displayed in GitHub](https://github.com/zetxek/adritian-free-hugo-theme/pull/272). If that's not the case, please [open a ticket in the theme issue tracker](https://github.com/zetxek/adritian-free-hugo-theme/issues).
|
||||
|
||||
## v1.7.8
|
||||
|
||||
This version introduces the `footer` content type to customize the content at the bottom of the page, allowing for sections (such as `contact-section` or `newsletter-section`) as well as content.
|
||||
|
||||
A new shortcode (`text-section`) is introduced, to enable nicely wrapped text in some places such as the homepage or the footer.
|
||||
|
||||
## v1.7.4
|
||||
|
||||
The "home" item in the navigation (footer and header) is managed now in the configuration file (`hugo.toml`), instead of a hardcoded item (which couldn't be hidden).
|
||||
|
||||
See the PR [#249 for the content change](https://github.com/zetxek/adritian-free-hugo-theme/pull/249), in case you want to add the home item back (or edit its name/title or translations).
|
||||
|
||||
## v1.7.3
|
||||
|
||||
The theme has got rid of the `2x` strategy for responsive images.
|
||||
They are now generated automatically from the large resolution picture.
|
||||
|
||||
This means that the partials using `2x` accept only one image now. There's a new attribute, `scale`, that defines how much larger the high-res image is compared to the smaller one.
|
||||
See the [PR #244 for more context](https://github.com/zetxek/adritian-free-hugo-theme/pull/244), and the [#198 PR in the demo repo for the content changes only](https://github.com/zetxek/adritian-demo/pull/198).
|
||||
|
||||
## v1.7.0
|
||||
|
||||
This version introduces a brand new way of managing pages, and leveraging the existing the styles in different contexts, so they can be used in other pages than the homepage.
|
||||
|
||||
The main changes are:
|
||||
|
||||
- deprecation of `homepage.yml`. Stopped using some of its values (such as `.Site.Data.homepage.newsletter.enable`, given that the shortcodes can be rendered in any page, not only the home)
|
||||
- introduction of numerous shortcodes, to replicate the same experience (in any page)
|
||||
|
||||
Aside from that:
|
||||
- "education" comes now from its own content type
|
||||
- fixed bugs where some buttons couldn't be hidden by not passing content
|
||||
- deprecated dynamic content in `i18n` files - in favour of storing it in markdown files. For example, your page headings or descriptions won't be stored in these files anymore.
|
||||
|
||||
As usual, I have "dog-fed" the theme into my own website first. You can [see the PR showing the change](https://github.com/zetxek/adrianmoreno.info/pull/291) it took to migrate. The most significant file (that you can "copy-paste") is [home.md](https://github.com/zetxek/adrianmoreno.info/pull/291/files#diff-34cdd7812bb042723b4068c4df80283586271078662d619aa33f88e8e62d6fd2
|
||||
).
|
||||
|
||||
You can also find a detailed PR for the demo site with [all the shortcodes in action](https://github.com/zetxek/adritian-demo/pull/182).
|
||||
|
||||
|
||||
Note: I have tried to keep backwards compatibility up to a point. But **I recommend considering migrating your site to shortcodes**, if you want to keep updated with the upstream version. Support for `homepage.yml` will be less prioritary, and retired at some point, because of the complexity in mantaining multiple ways of passing content to the theme.
|
||||
|
||||
🛑 Alternatively, fix your theme to the last version `v1.6.1`, to prevent upgrading (losing the new features and improvements).
|
||||
|
||||
## v1.6.0
|
||||
|
||||
This version introduces the usage of [@zetxek/adritian-theme-helper](https://www.npmjs.com/package/adritian-theme-helper) to help initialize a site with the theme. This is not of much use to existing setups - but good to know.
|
||||
|
||||
Additionally, there are a number of [changes in HTML structure](https://github.com/zetxek/adritian-free-hugo-theme/pull/217/files) to ensure that the content is valid, including class renaming and change of element types.
|
||||
|
||||
Some of the changes are listed here in a an attempt to help you edit your custom CSS in case you depended on it:
|
||||
|
||||
- Prevent `p.lead` usage, preferring `div.lead` instead. This is done to prevent nested `<p>` elements or broken paragraphs, using a wrapping `div` instead.
|
||||
- Fixed nested `main` elements in the `/blog` page, using instead `<main><div#main-content>`.
|
||||
- Prevented duplicated usage of the `#experience` id, by renaming it to `#experience-single` in `layouts/partials/experience.html` and `#experience-list` in `layouts/shortcodes/experience-list.html`.
|
||||
- Removed wrapping `<p>` element in `layouts/partials/testimonial.html`.
|
||||
|
||||
I hope this doesn't cause too much trouble in your existing sites - and allows for a smooth transition to `v1.6.0` 🤞
|
||||
|
||||
## v1.5.12
|
||||
|
||||
The options to control how the language and theme selectors are displayed in the header and footer have been refactored, to enable control over each placement individually (footer and header).
|
||||
|
||||
There are 3 placements that can be controlled. For the color selector, it's always available; for the languae selector, it's available if the site is multilingual.
|
||||
|
||||
The configuration syntax is as follows:
|
||||
```
|
||||
[params.languages.selector.disable]
|
||||
footer = false
|
||||
header = false
|
||||
mobileHeader = false
|
||||
|
||||
[params.colorTheme.selector.disable]
|
||||
footer = false
|
||||
header = false
|
||||
mobileHeader = false
|
||||
```
|
||||
|
||||
The default value is assumed to be `false` for all placements, meaning that the selector is displayed in all placements by default.
|
||||
|
||||
## v1.5.7
|
||||
|
||||
In order to have the (optional) print improvements for the CV, you need to add the following to your `config.toml` file:
|
||||
|
||||
In the mounts section:
|
||||
```
|
||||
[[module.mounts]]
|
||||
source = "node_modules/bootstrap-print-css/css/bootstrap-print.css"
|
||||
target = "assets/css/bootstrap-print.css"
|
||||
```
|
||||
|
||||
In the `params` section, under the `plugins` section, add the following:
|
||||
|
||||
```
|
||||
[[params.plugins.css]]
|
||||
URL = "css/bootstrap-print.css"
|
||||
```
|
||||
|
||||
Note: this will add about 1.1Kb to the total size of the CSS loaded.
|
||||
|
||||
## v1.5.3
|
||||
|
||||
A new parameter, `showJobCard`, has been added to the `experience` section in the `config.toml` file.
|
||||
This allows you to hide the job card on the experience page.
|
||||
See the [related PR #271](https://github.com/zetxek/adritian-free-hugo-theme/pull/182) for more information.
|
||||
|
||||
## v1.5.2
|
||||
|
||||
Added a new (optional) parameter, "logo", to display an image in the header.
|
||||
See the [related PR #179](https://github.com/zetxek/adritian-free-hugo-theme/pull/179) for more information.
|
||||
|
||||
## v1.5.1
|
||||
|
||||
The theme uses bootstrap as a dependency, installed from npm.
|
||||
This requires some steps in the site:
|
||||
|
||||
1. copy the theme `package.json` to your site, and run `npm install`
|
||||
2. add the following section to the `hugo.toml` configuration to your site:
|
||||
|
||||
```
|
||||
[[module.mounts]]
|
||||
source = "node_modules/bootstrap/scss"
|
||||
target = "assets/scss/bootstrap"
|
||||
|
||||
[[module.mounts]]
|
||||
source = "node_modules/bootstrap/dist/js"
|
||||
target = "assets/js/bootstrap"
|
||||
|
||||
[[module.mounts]]
|
||||
source = "node_modules/bootstrap/dist/js/bootstrap.bundle.min.js"
|
||||
target = "assets/js/vendor/bootstrap.bundle.min.js"
|
||||
```
|
||||
|
||||
|
||||
## v1.5.0
|
||||
|
||||
The theme has been updated to support Hugo modules.
|
||||
This is now the recommended way to install the theme, as it allows for easier updates and contributions.
|
||||
|
||||
See https://github.com/zetxek/adrianmoreno.info/pull/270 for an example of how to update an existing site to switch from git submodule to Hugo module.
|
||||
|
||||
Some key steps:
|
||||
- initialize the module in your site: `hugo mod init github.com/username/your-site`
|
||||
- add the module to your `hugo.toml` file: `[[module.imports]] path = "github.com/zetxek/adritian-free-hugo-theme"`
|
||||
- get the module: `hugo mod get -u`
|
||||
|
||||
To use a specific version of the theme, you can add the version to the module import: `[[module.imports]] path = "github.com/zetxek/adritian-free-hugo-theme@v2.0.0"`
|
||||
To use an unpublished version of the theme, you can add the git reference to the `go.mod` file: `require github.com/zetxek/adritian-free-hugo-theme <any-git-reference>`, and then execute `hugo mod get -u`.
|
||||
|
||||
|
||||
**Note**: if you use vercel to host your site, you will need to make sure that `go` is installed in the vercel build environment.
|
||||
You can do this by adding the following to your `vercel.json` file: `"installCommand": "dnf -y install golang",`.
|
||||
|
||||
## v1.4.13
|
||||
|
||||
### Analytics section re-organized
|
||||
|
||||
In the [PR #121](https://github.com/zetxek/adritian-free-hugo-theme/pull/121), we moved the analytics config to a new section in the configuration for better extensibility and clarity.
|
||||
|
||||
Before:
|
||||
```
|
||||
vercelPageInsights = false
|
||||
vercelAnalytics = false
|
||||
|
||||
[params.google_analytics]
|
||||
code = "UA-XXXXX-Y"
|
||||
enabled = false
|
||||
[params.google_tag_manager]
|
||||
code = "GTM-XXXXX"
|
||||
enabled = false
|
||||
```
|
||||
|
||||
After:
|
||||
```
|
||||
[params.analytics]
|
||||
## Analytics parameters.
|
||||
### Supported so far: Vercel (Page Insights, Analytics)
|
||||
### And Google (Tag Manager, Analytics)
|
||||
|
||||
# controls vercel page insights - disabled by default
|
||||
# to enable, just set to true
|
||||
vercelPageInsights = false
|
||||
vercelAnalytics = false
|
||||
|
||||
# google analytics and tag manager. to enable, set "enabled" to true
|
||||
# and add the tracking code (UA-something for analytics, GTM-something for tag manager)
|
||||
[params.analytics.googleAnalytics]
|
||||
code = "UA-XXXXX-Y"
|
||||
enabled = true
|
||||
[params.analytics.googleTagManager]
|
||||
code = "GTM-XXXXX"
|
||||
enabled = false
|
||||
```
|
||||
|
||||
See the theme's [`exampleSite` hugo.toml](https://github.com/zetxek/adritian-free-hugo-theme/blob/main/exampleSite/hugo.toml) file for an example configuration.
|
||||
|
||||
### "Experience buttons" can be hidden
|
||||
|
||||
A new configuration option has been added to the experience buttons.
|
||||
|
||||
Before:
|
||||
```
|
||||
experience:
|
||||
button:
|
||||
enable: true
|
||||
icon: "icon-linkedin"
|
||||
## the url and text are localized, fill them in the i18n file
|
||||
## keys: experience_button, experience_button_url
|
||||
```
|
||||
|
||||
After:
|
||||
```
|
||||
experience:
|
||||
enable: true
|
||||
button:
|
||||
enable: true
|
||||
icon: "icon-linkedin"
|
||||
## the url and text are localized, fill them in the i18n file
|
||||
## keys: experience_button, experience_button_url
|
||||
```
|
||||
|
||||
## v1.4.9
|
||||
|
||||
### Change 1: new translations keys
|
||||
|
||||
Added for the color switcher in the footer
|
||||
- theme color switcher (`toggle_theme`) and themes (`theme_light`, `theme_dark`, `theme_auto`).
|
||||
|
||||
### Change 2: css files refactor
|
||||
|
||||
This version continues to align with Bootstrap extension capabilities.
|
||||
You will need to change the import of CSS files in your `config.toml` file, in the `Plugins` section.
|
||||
|
||||
**Before:**
|
||||
```
|
||||
# CSS Plugins
|
||||
[[params.plugins.css]]
|
||||
URL = "css/main.css"
|
||||
[[params.plugins.css]]
|
||||
URL = "css/custom.css"
|
||||
[[params.plugins.css]]
|
||||
URL = "css/adritian-icons.css"
|
||||
|
||||
# JS Plugins
|
||||
[[params.plugins.js]]
|
||||
URL = "js/rad-animations.js"
|
||||
[[params.plugins.js]]
|
||||
URL = "js/sticky-header.js"
|
||||
[[params.plugins.js]]
|
||||
URL = "js/library/fontfaceobserver.js"
|
||||
|
||||
# SCSS Plugins
|
||||
[[params.plugins.scss]]
|
||||
URL = "scss/adritian.scss"
|
||||
```
|
||||
|
||||
**After:**
|
||||
|
||||
See that the `main.css` file is gone from CSS:
|
||||
```
|
||||
# CSS Plugins
|
||||
[[params.plugins.css]]
|
||||
URL = "css/custom.css"
|
||||
[[params.plugins.css]]
|
||||
URL = "css/adritian-icons.css"
|
||||
|
||||
# JS Plugins
|
||||
[[params.plugins.js]]
|
||||
URL = "js/rad-animations.js"
|
||||
[[params.plugins.js]]
|
||||
URL = "js/sticky-header.js"
|
||||
[[params.plugins.js]]
|
||||
URL = "js/library/fontfaceobserver.js"
|
||||
|
||||
# SCSS Plugins
|
||||
[[params.plugins.scss]]
|
||||
URL = "scss/adritian.scss"
|
||||
```
|
||||
|
||||
Also, added new parameters in `config.toml`:
|
||||
|
||||
```
|
||||
# theme/color style
|
||||
[params.colorTheme]
|
||||
|
||||
## the following configuration would disable automatic theme selection
|
||||
# [params.colorTheme.auto]
|
||||
# disable = true
|
||||
# [params.colorTheme.forced]
|
||||
# theme = "dark"
|
||||
|
||||
## the following parameter will disable theme override in the footer
|
||||
# [params.colorTheme.selector.disable]
|
||||
# footer = true
|
||||
|
||||
|
||||
## by default we allow override AND automatic selection
|
||||
```
|
||||
|
||||
## v1.4.5
|
||||
|
||||
This version has aligned more the custom CSS with Bootstrap's extension capabilities.
|
||||
You will need to change the import of CSS files in your `config.toml` file, in the `Plugins` section.
|
||||
|
||||
**Before:**
|
||||
|
||||
```
|
||||
# CSS Plugins
|
||||
[[params.plugins.css]]
|
||||
URL = "css/main.css"
|
||||
[[params.plugins.css]]
|
||||
URL = "css/adritian.css"
|
||||
[[params.plugins.css]]
|
||||
URL = "css/adritian-icons.css"
|
||||
[[params.plugins.css]]
|
||||
URL = "css/custom.css"
|
||||
|
||||
(...)
|
||||
|
||||
# SCSS Plugins
|
||||
[[params.plugins.scss]]
|
||||
URL = "scss/menu.scss"
|
||||
[[params.plugins.scss]]
|
||||
URL = "scss/bootstrap/bootstrap.scss"
|
||||
```
|
||||
|
||||
**After:**
|
||||
(note that `adritian.css` gets moved to the `scss` section, and it's not needed to import `bootstrap.scss` or `menu.scss` manually - as they are not included in `adritian.scss`)
|
||||
```
|
||||
# CSS Plugins
|
||||
[[params.plugins.css]]
|
||||
URL = "css/main.css"
|
||||
[[params.plugins.css]]
|
||||
URL = "css/custom.css"
|
||||
[[params.plugins.css]]
|
||||
URL = "css/adritian-icons.css"
|
||||
|
||||
# JS Plugins
|
||||
[[params.plugins.js]]
|
||||
URL = "js/rad-animations.js"
|
||||
[[params.plugins.js]]
|
||||
URL = "js/sticky-header.js"
|
||||
[[params.plugins.js]]
|
||||
URL = "js/library/fontfaceobserver.js"
|
||||
|
||||
# SCSS Plugins
|
||||
[[params.plugins.scss]]
|
||||
URL = "scss/adritian.scss"
|
||||
```
|
||||
|
||||
## v1.4.0
|
||||
|
||||
This version switches from the legacy, "embedded" Boostrap based on v4.3.1 (from the [original codebase](https://github.com/radity/raditian-free-hugo-theme/blob/daa341d4156986787611a01d075ca94233ff4d3b/static/css/main.css)) to the Scss-based version, [v5.3.3](https://getbootstrap.com/docs/5.3) (ast per december'24).
|
||||
|
||||
This requires some small adjustments in the `config.toml` file.
|
||||
|
||||
### Add build stats
|
||||
Add the following:
|
||||
```
|
||||
[build]
|
||||
[build.buildStats]
|
||||
disableClasses = false
|
||||
disableIDs = false
|
||||
disableTags = false
|
||||
enable = true
|
||||
```
|
||||
|
||||
### Add new `adritian.css` file
|
||||
|
||||
Add the following, under the `params.plugins.css` section:
|
||||
|
||||
```
|
||||
[[params.plugins.css]]
|
||||
URL = "css/adritian.css"
|
||||
```
|
||||
|
||||
### ⚠️ Setup boostrap as Scss
|
||||
|
||||
Add the following, under the `params.plugins.scss` section:
|
||||
|
||||
```
|
||||
[[params.plugins.scss]]
|
||||
URL = "scss/bootstrap/bootstrap.scss"
|
||||
```
|
||||
|
||||
See the contents of the [PR #94 in the demo site](https://github.com/zetxek/adritian-demo/pull/94/files) as an example.
|
||||
|
||||
## v1.3.4
|
||||
|
||||
In `config.toml`, replace
|
||||
```
|
||||
[[params.plugins.css]]
|
||||
URL = "css/rad-icons.css"
|
||||
```
|
||||
by
|
||||
```
|
||||
[[params.plugins.css]]
|
||||
URL = "css/adritian-icons.css"
|
||||
```
|
||||
|
||||
In your config/content files, replace:
|
||||
- `icon-linkedin-fill` by `icon-linkedin`
|
||||
- `icon-smile-fill` by `icon-user`
|
||||
- `icon-arrow-right` by `icon-circle-arrow-right`
|
||||
- `icon-mail-fill` by `icon-email`
|
||||
|
||||
|
||||
## v1.2.x to v1.3.x
|
||||
|
||||
In `v1.3.x`, the [support for internationalization was added](https://github.com/zetxek/adritian-free-hugo-theme/pull/78).
|
||||
This means that some of the content previously managed in the `homepage.yaml` file is now managed in `i18n/{language}.yaml`.
|
||||
|
||||
For example:
|
||||
- the main page title was managed in `homepage.yml`:`showcase.title`, now it's managed in `i18n/{language}.yaml`:`logo_text1`
|
||||
- the page logo was managed in `hugo.toml`:`params.logo` (`text1` and `text2`), now it's managed in `i18n/{language}.yaml`:`logo_text1` and `logo_text2`.
|
||||
|
||||
The reason for the change is to allow overriding almost any string in the theme in different content translations.
|
||||
@@ -0,0 +1,14 @@
|
||||
+++
|
||||
title = "{{ replace .Name "-" " " | title }}"
|
||||
date = {{ .Date }}
|
||||
draft = true
|
||||
featured = false
|
||||
weight = 100 # Lower weight appears first in featured sections
|
||||
description = ""
|
||||
[images]
|
||||
featured_image = "" # Path to the featured image
|
||||
tags = []
|
||||
topics = []
|
||||
+++
|
||||
|
||||
Add your content here.
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
date: '{{ .Date }}' # date in which the content is created - defaults to "today"
|
||||
title: '{{ replace .File.ContentBaseName `-` ` ` | title }}'
|
||||
draft: false # set to "true" if you want to hide the content
|
||||
|
||||
link: "https://www.adrianmoreno.info" # optional URL to link the logo to
|
||||
|
||||
params:
|
||||
logo:
|
||||
src: "" # example: "images/clients/asgardia.png"
|
||||
scale: 0.5
|
||||
|
||||
## The content is not used (yet). If you have ideas on how to use it,
|
||||
## you can suggest it at https://github.com/zetxek/adritian-free-hugo-theme/discussions
|
||||
---
|
||||
@@ -0,0 +1,2 @@
|
||||
+++
|
||||
+++
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
date: '{{ .Date }}' # date in which the content is created - defaults to "today"
|
||||
title: '{{ replace .File.ContentBaseName `-` ` ` | title }}'
|
||||
draft: false # set to "true" if you want to hide the content
|
||||
jobTitle: "" # job description/title. Fill-in
|
||||
company: "" # name of the company you worked for. Fill-in
|
||||
location: "" # place/city/country for the experience. Fill-in.
|
||||
duration: "" # from-to, for example "2022-2024". Fill-in.
|
||||
|
||||
## For the content, you can use a title and a job description.
|
||||
## For example:
|
||||
# ### Fixing the world, one byte at a time
|
||||
# The beginning of a great career.
|
||||
#
|
||||
---
|
||||
@@ -0,0 +1,16 @@
|
||||
---
|
||||
date: '{{ .Date }}' # date in which the content is created - defaults to "today"
|
||||
title: '{{ replace .File.ContentBaseName `-` ` ` | title }}'
|
||||
draft: false # set to "true" if you want to hide the content
|
||||
logo_x: "" # example: "images/clients/asgardia.png"
|
||||
link: "https://www.adrianmoreno.info" # optional URL to link the logo to
|
||||
|
||||
params:
|
||||
button:
|
||||
icon: ""
|
||||
btnText: ""
|
||||
URL: ""
|
||||
image:
|
||||
src: ""
|
||||
## The content is used for the description of the project
|
||||
---
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
date: '{{ .Date }}' # date in which the content is created - defaults to "today"
|
||||
title: '{{ replace .File.ContentBaseName `-` ` ` | title }}'
|
||||
draft: false # set to "true" if you want to hide the content
|
||||
|
||||
name: "" # place/city/country for the experience. Fill-in.
|
||||
position: "" # from-to, for example "2022-2024". Fill-in.
|
||||
|
||||
params:
|
||||
logo:
|
||||
src: "" # example: "images/clients/asgardia.png"
|
||||
|
||||
## For the content, you can use markdown
|
||||
##
|
||||
---
|
||||
@@ -0,0 +1,34 @@
|
||||
|
||||
.icon-threads:before { content: '\e800'; } /* '' */
|
||||
.icon-bluesky:before { content: '\e801'; } /* '' */
|
||||
.icon-x-twitter:before { content: '\e802'; } /* '' */
|
||||
.icon-email:before { content: '\e803'; } /* '' */
|
||||
.icon-tiktok:before { content: '\e804'; } /* '' */
|
||||
.icon-search:before { content: '\e805'; } /* '' */
|
||||
.icon-user:before { content: '\f007'; } /* '' */
|
||||
.icon-table-list:before { content: '\f00b'; } /* '' */
|
||||
.icon-download:before { content: '\f019'; } /* '' */
|
||||
.icon-circle-info:before { content: '\f05a'; } /* '' */
|
||||
.icon-square-twitter:before { content: '\f081'; } /* '' */
|
||||
.icon-square-facebook:before { content: '\f082'; } /* '' */
|
||||
.icon-linkedin:before { content: '\f08c'; } /* '' */
|
||||
.icon-square-github:before { content: '\f092'; } /* '' */
|
||||
.icon-circle-arrow-left:before { content: '\f0a8'; } /* '' */
|
||||
.icon-circle-arrow-right:before { content: '\f0a9'; } /* '' */
|
||||
.icon-circle-arrow-up:before { content: '\f0aa'; } /* '' */
|
||||
.icon-circle-arrow-down:before { content: '\f0ab'; } /* '' */
|
||||
.icon-quote-left:before { content: '\f10d'; } /* '' */
|
||||
.icon-face-smile:before { content: '\f118'; } /* '' */
|
||||
.icon-square-arrow-up-right:before { content: '\f14c'; } /* '' */
|
||||
.icon-youtube:before { content: '\f167'; } /* '' */
|
||||
.icon-square-xing:before { content: '\f169'; } /* '' */
|
||||
.icon-stack-overflow:before { content: '\f16c'; } /* '' */
|
||||
.icon-instagram:before { content: '\f16d'; } /* '' */
|
||||
.icon-dribbble:before { content: '\f17d'; } /* '' */
|
||||
.icon-behance:before { content: '\f1b4'; } /* '' */
|
||||
.icon-file-pdf:before { content: '\f1c1'; } /* '' */
|
||||
.icon-codepen:before { content: '\f1cb'; } /* '' */
|
||||
.icon-yelp:before { content: '\f1e9'; } /* '' */
|
||||
.icon-medium:before { content: '\f23a'; } /* '' */
|
||||
.icon-meetup:before { content: '\f2e0'; } /* '' */
|
||||
.icon-cloud-arrow-down:before { content: '\f381'; } /* '' */
|
||||
@@ -0,0 +1,89 @@
|
||||
@font-face {
|
||||
font-family: 'adritian-icons';
|
||||
src: url('../fonts/adritian-icons.eot?62019798');
|
||||
src: url('../fonts/adritian-icons.eot?62019798#iefix') format('embedded-opentype'),
|
||||
url('../fonts/adritian-icons.woff2?62019798') format('woff2'),
|
||||
url('../fonts/adritian-icons.woff?62019798') format('woff'),
|
||||
url('../fonts/adritian-icons.ttf?62019798') format('truetype'),
|
||||
url('../fonts/adritian-icons.svg?62019798#adritian-icons') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
/* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */
|
||||
/* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */
|
||||
/*
|
||||
@media screen and (-webkit-min-device-pixel-ratio:0) {
|
||||
@font-face {
|
||||
font-family: 'adritian-icons';
|
||||
src: url('../fonts/adritian-icons.svg?62019798#adritian-icons') format('svg');
|
||||
}
|
||||
}
|
||||
*/
|
||||
[class^="icon-"]:before, [class*=" icon-"]:before {
|
||||
font-family: "adritian-icons";
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
speak: never;
|
||||
|
||||
display: inline-block;
|
||||
text-decoration: inherit;
|
||||
width: 1em;
|
||||
margin-right: .2em;
|
||||
text-align: center;
|
||||
/* opacity: .8; */
|
||||
|
||||
/* For safety - reset parent styles, that can break glyph codes*/
|
||||
font-variant: normal;
|
||||
text-transform: none;
|
||||
|
||||
/* fix buttons height, for twitter bootstrap */
|
||||
line-height: 1em;
|
||||
|
||||
/* Animation center compensation - margins should be symmetric */
|
||||
/* remove if not needed */
|
||||
margin-left: .2em;
|
||||
|
||||
/* you can be more comfortable with increased icons size */
|
||||
/* font-size: 120%; */
|
||||
|
||||
/* Font smoothing. That was taken from TWBS */
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
|
||||
/* Uncomment for 3D effect */
|
||||
/* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */
|
||||
}
|
||||
|
||||
.icon-threads:before { content: '\e800'; } /* '' */
|
||||
.icon-bluesky:before { content: '\e801'; } /* '' */
|
||||
.icon-x-twitter:before { content: '\e802'; } /* '' */
|
||||
.icon-email:before { content: '\e803'; } /* '' */
|
||||
.icon-tiktok:before { content: '\e804'; } /* '' */
|
||||
.icon-search:before { content: '\e805'; } /* '' */
|
||||
.icon-user:before { content: '\f007'; } /* '' */
|
||||
.icon-table-list:before { content: '\f00b'; } /* '' */
|
||||
.icon-download:before { content: '\f019'; } /* '' */
|
||||
.icon-circle-info:before { content: '\f05a'; } /* '' */
|
||||
.icon-square-twitter:before { content: '\f081'; } /* '' */
|
||||
.icon-square-facebook:before { content: '\f082'; } /* '' */
|
||||
.icon-linkedin:before { content: '\f08c'; } /* '' */
|
||||
.icon-square-github:before { content: '\f092'; } /* '' */
|
||||
.icon-circle-arrow-left:before { content: '\f0a8'; } /* '' */
|
||||
.icon-circle-arrow-right:before { content: '\f0a9'; } /* '' */
|
||||
.icon-circle-arrow-up:before { content: '\f0aa'; } /* '' */
|
||||
.icon-circle-arrow-down:before { content: '\f0ab'; } /* '' */
|
||||
.icon-quote-left:before { content: '\f10d'; } /* '' */
|
||||
.icon-face-smile:before { content: '\f118'; } /* '' */
|
||||
.icon-square-arrow-up-right:before { content: '\f14c'; } /* '' */
|
||||
.icon-youtube:before { content: '\f167'; } /* '' */
|
||||
.icon-square-xing:before { content: '\f169'; } /* '' */
|
||||
.icon-stack-overflow:before { content: '\f16c'; } /* '' */
|
||||
.icon-instagram:before { content: '\f16d'; } /* '' */
|
||||
.icon-dribbble:before { content: '\f17d'; } /* '' */
|
||||
.icon-behance:before { content: '\f1b4'; } /* '' */
|
||||
.icon-file-pdf:before { content: '\f1c1'; } /* '' */
|
||||
.icon-codepen:before { content: '\f1cb'; } /* '' */
|
||||
.icon-yelp:before { content: '\f1e9'; } /* '' */
|
||||
.icon-medium:before { content: '\f23a'; } /* '' */
|
||||
.icon-meetup:before { content: '\f2e0'; } /* '' */
|
||||
.icon-cloud-arrow-down:before { content: '\f381'; } /* '' */
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
Animation example, for spinners
|
||||
*/
|
||||
.animate-spin {
|
||||
-moz-animation: spin 2s infinite linear;
|
||||
-o-animation: spin 2s infinite linear;
|
||||
-webkit-animation: spin 2s infinite linear;
|
||||
animation: spin 2s infinite linear;
|
||||
display: inline-block;
|
||||
}
|
||||
@-moz-keyframes spin {
|
||||
0% {
|
||||
-moz-transform: rotate(0deg);
|
||||
-o-transform: rotate(0deg);
|
||||
-webkit-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
-moz-transform: rotate(359deg);
|
||||
-o-transform: rotate(359deg);
|
||||
-webkit-transform: rotate(359deg);
|
||||
transform: rotate(359deg);
|
||||
}
|
||||
}
|
||||
@-webkit-keyframes spin {
|
||||
0% {
|
||||
-moz-transform: rotate(0deg);
|
||||
-o-transform: rotate(0deg);
|
||||
-webkit-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
-moz-transform: rotate(359deg);
|
||||
-o-transform: rotate(359deg);
|
||||
-webkit-transform: rotate(359deg);
|
||||
transform: rotate(359deg);
|
||||
}
|
||||
}
|
||||
@-o-keyframes spin {
|
||||
0% {
|
||||
-moz-transform: rotate(0deg);
|
||||
-o-transform: rotate(0deg);
|
||||
-webkit-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
-moz-transform: rotate(359deg);
|
||||
-o-transform: rotate(359deg);
|
||||
-webkit-transform: rotate(359deg);
|
||||
transform: rotate(359deg);
|
||||
}
|
||||
}
|
||||
@-ms-keyframes spin {
|
||||
0% {
|
||||
-moz-transform: rotate(0deg);
|
||||
-o-transform: rotate(0deg);
|
||||
-webkit-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
-moz-transform: rotate(359deg);
|
||||
-o-transform: rotate(359deg);
|
||||
-webkit-transform: rotate(359deg);
|
||||
transform: rotate(359deg);
|
||||
}
|
||||
}
|
||||
@keyframes spin {
|
||||
0% {
|
||||
-moz-transform: rotate(0deg);
|
||||
-o-transform: rotate(0deg);
|
||||
-webkit-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
-moz-transform: rotate(359deg);
|
||||
-o-transform: rotate(359deg);
|
||||
-webkit-transform: rotate(359deg);
|
||||
transform: rotate(359deg);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
/* placeholder */
|
||||
@@ -0,0 +1 @@
|
||||
/* placeholder file: to be populated by npm scripts */
|
||||
@@ -0,0 +1 @@
|
||||
/* You can customize theme styles here */
|
||||
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="14" viewBox="0 0 16 14">
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<path d="M-1-2h18v18H-1z"/>
|
||||
<path fill="#FFF" fill-rule="nonzero" d="M1.25.25h13.5a.75.75 0 0 1 .75.75v12a.75.75 0 0 1-.75.75H1.25A.75.75 0 0 1 .5 13V1a.75.75 0 0 1 .75-.75zm6.795 6.512L3.236 2.68l-.971 1.143 5.79 4.916 5.686-4.92-.982-1.135-4.713 4.08h-.001z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 419 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<path d="M-1-1h18v18H-1z"/>
|
||||
<path fill="#FFF" fill-rule="nonzero" d="M8 15.5a7.5 7.5 0 1 1 0-15 7.5 7.5 0 0 1 0 15zM4.25 8a3.75 3.75 0 0 0 7.5 0h-1.5a2.25 2.25 0 1 1-4.5 0h-1.5z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 337 B |
|
After Width: | Height: | Size: 344 KiB |
@@ -0,0 +1,98 @@
|
||||
/*!
|
||||
* Color mode toggler for Bootstrap's docs (https://getbootstrap.com/)
|
||||
* Copyright 2011-2024 The Bootstrap Authors
|
||||
* Licensed under the Creative Commons Attribution 3.0 Unported License.
|
||||
*/
|
||||
|
||||
(() => {
|
||||
'use strict'
|
||||
|
||||
console.log('Dark mode toggle script loaded')
|
||||
|
||||
const getStoredTheme = () => localStorage.getItem('theme')
|
||||
const setStoredTheme = theme => localStorage.setItem('theme', theme)
|
||||
|
||||
const getPreferredTheme = () => {
|
||||
const storedTheme = getStoredTheme()
|
||||
if (storedTheme) {
|
||||
return storedTheme
|
||||
}
|
||||
|
||||
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
|
||||
}
|
||||
|
||||
function setTheme(theme) {
|
||||
// Apply a transition class before changing theme
|
||||
document.documentElement.classList.add('theme-transition');
|
||||
|
||||
// Set the new theme
|
||||
if (theme === 'auto') {
|
||||
const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
document.documentElement.setAttribute('data-bs-theme', isDark ? 'dark' : 'light');
|
||||
// Store the actual value for smoother transitions
|
||||
document.documentElement.setAttribute('data-theme-auto', 'true');
|
||||
} else {
|
||||
document.documentElement.setAttribute('data-bs-theme', theme);
|
||||
document.documentElement.removeAttribute('data-theme-auto');
|
||||
}
|
||||
|
||||
// Remove the transition class after a short delay
|
||||
setTimeout(() => {
|
||||
document.documentElement.classList.remove('theme-transition');
|
||||
}, 300);
|
||||
}
|
||||
|
||||
// Apply theme immediately to prevent flash
|
||||
setTheme(getPreferredTheme())
|
||||
|
||||
const showActiveTheme = (theme, focus = false) => {
|
||||
const themeSwitcher = document.querySelectorAll('.bd-theme-selector')
|
||||
|
||||
if (!themeSwitcher) {
|
||||
return
|
||||
}
|
||||
|
||||
const themeSwitcherText = document.querySelectorAll('.bd-theme-text')
|
||||
const activeTheme = document.querySelectorAll('.current-theme')
|
||||
const btnToActive = document.querySelectorAll(`[data-bs-theme-value="${theme}"]`)
|
||||
//const iconOfActiveBtn = btnToActive.querySelector('span.theme-icon')
|
||||
|
||||
|
||||
document.querySelectorAll('[data-bs-theme-value]').forEach(element => {
|
||||
element.classList.remove('active')
|
||||
element.setAttribute('aria-pressed', 'false')
|
||||
})
|
||||
for (const element of btnToActive){
|
||||
element.setAttribute('aria-pressed', 'true')
|
||||
}
|
||||
for (const element of activeTheme) {
|
||||
element.textContent = btnToActive[0].textContent
|
||||
}
|
||||
const themeSwitcherLabel = `${themeSwitcherText[0].textContent} (${btnToActive[0].dataset.bsThemeValue})`
|
||||
for (const element of themeSwitcher) {
|
||||
element.setAttribute('aria-label', themeSwitcherLabel)
|
||||
}
|
||||
}
|
||||
|
||||
// Listen for system preference changes
|
||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
|
||||
const storedTheme = getStoredTheme()
|
||||
if (storedTheme === 'auto' || (!storedTheme && document.documentElement.getAttribute('data-theme-auto') === 'true')) {
|
||||
setTheme('auto')
|
||||
}
|
||||
})
|
||||
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
showActiveTheme(getPreferredTheme())
|
||||
|
||||
document.querySelectorAll('[data-bs-theme-value]')
|
||||
.forEach(toggle => {
|
||||
toggle.addEventListener('click', () => {
|
||||
const theme = toggle.getAttribute('data-bs-theme-value')
|
||||
setStoredTheme(theme)
|
||||
setTheme(theme)
|
||||
showActiveTheme(theme, true)
|
||||
})
|
||||
})
|
||||
})
|
||||
})()
|
||||
@@ -0,0 +1,4 @@
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
gtag('js', new Date());
|
||||
gtag('config', 'UA-000000-0');
|
||||
@@ -0,0 +1,56 @@
|
||||
// Wait for the DOM content to be fully loaded
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Create the overlay element if it doesn't exist
|
||||
if (!document.querySelector('.transition-overlay')) {
|
||||
const overlay = document.createElement('div');
|
||||
overlay.className = 'transition-overlay';
|
||||
document.body.appendChild(overlay);
|
||||
}
|
||||
|
||||
// Find all internal links
|
||||
for (const link of document.querySelectorAll('a')) {
|
||||
const href = link.getAttribute('href');
|
||||
|
||||
// Skip if no href
|
||||
if (!href) continue;
|
||||
|
||||
// Skip anchor links (both relative #section and absolute with fragments)
|
||||
if (href.startsWith('#') ||
|
||||
(link.hostname === window.location.hostname &&
|
||||
link.pathname === window.location.pathname &&
|
||||
link.hash)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Only handle links to the same site that aren't anchor links
|
||||
if (link.hostname === window.location.hostname &&
|
||||
!link.hasAttribute('target')) {
|
||||
|
||||
link.addEventListener('click', (e) => {
|
||||
// Don't handle special clicks (modifier keys)
|
||||
if (e.ctrlKey || e.shiftKey || e.metaKey || e.altKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
const destination = link.href;
|
||||
|
||||
// Add active class to trigger the overlay
|
||||
document.body.classList.add('transition-active');
|
||||
|
||||
// Navigate after a slight delay
|
||||
setTimeout(() => {
|
||||
window.location.href = destination;
|
||||
}, 300);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// If someone navigates back/forward, we need to hide the overlay
|
||||
window.addEventListener('pageshow', (event) => {
|
||||
// This works for both fresh loads and back-forward cache
|
||||
if (document.body.classList.contains('transition-active')) {
|
||||
document.body.classList.remove('transition-active');
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,373 @@
|
||||
|
||||
|
||||
|
||||
.blog {
|
||||
|
||||
.post .post-title a {
|
||||
color: $base-color;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.has-sidebar {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 2rem;
|
||||
|
||||
@media (min-width: 768px) {
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
// Add specific ordering for sidebar right layout
|
||||
.col-sidebar {
|
||||
width: 100%;
|
||||
min-width: 280px;
|
||||
|
||||
@media (min-width: 768px) {
|
||||
min-width: 250px;
|
||||
}
|
||||
|
||||
@media (max-width: 380px) {
|
||||
min-width: 240px;
|
||||
}
|
||||
}
|
||||
|
||||
.col-content {
|
||||
&.narrow-content {
|
||||
max-width: 720px;
|
||||
}
|
||||
}
|
||||
|
||||
// Blog post readability improvements
|
||||
.post-summary,
|
||||
.post-content {
|
||||
line-height: 1.75;
|
||||
font-size: 1.125rem; // 18px
|
||||
color: var(--bs-body-color);
|
||||
|
||||
p {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
margin-top: 2rem;
|
||||
margin-bottom: 1rem;
|
||||
line-height: 1.3;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
h1 { font-size: 2.5rem; }
|
||||
h2 { font-size: 2rem; }
|
||||
h3 { font-size: 1.75rem; }
|
||||
h4 { font-size: 1.5rem; }
|
||||
h5 { font-size: 1.25rem; }
|
||||
h6 { font-size: 1.125rem; }
|
||||
|
||||
ul, ol {
|
||||
margin-bottom: 1.5rem;
|
||||
|
||||
li {
|
||||
margin-bottom: 0.75rem;
|
||||
line-height: 1.75;
|
||||
}
|
||||
}
|
||||
|
||||
blockquote {
|
||||
border-left: 4px solid var(--bs-primary);
|
||||
padding-left: 1.5rem;
|
||||
margin: 2rem 0;
|
||||
font-style: italic;
|
||||
color: var(--bs-secondary-color);
|
||||
}
|
||||
|
||||
code {
|
||||
background-color: var(--bs-gray-100);
|
||||
padding: 0.2em 0.4em;
|
||||
border-radius: 3px;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
pre {
|
||||
background-color: var(--bs-gray-100);
|
||||
padding: 1rem;
|
||||
border-radius: 0.375rem;
|
||||
overflow-x: auto;
|
||||
margin: 1.5rem 0;
|
||||
|
||||
code {
|
||||
background-color: transparent;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.blog-sidebar {
|
||||
position: sticky;
|
||||
top: 2rem;
|
||||
|
||||
.sidebar-section {
|
||||
margin-bottom: 2rem;
|
||||
padding: 1.5rem;
|
||||
background: #edf1f4;
|
||||
border-radius: 0.5rem;
|
||||
|
||||
// Increase padding and set minimum width on smaller screens
|
||||
@media (max-width: 767px) {
|
||||
padding: 2rem;
|
||||
min-width: 280px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@media (max-width: 380px) {
|
||||
min-width: 240px;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin-bottom: 1rem;
|
||||
font-size: 1.2rem;
|
||||
font-weight: 600;
|
||||
color: var(--bs-gray-900);
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
||||
li {
|
||||
margin-bottom: 0.75rem;
|
||||
|
||||
a {
|
||||
color: $sidebar-link-color;
|
||||
transition: color 0.2s ease;
|
||||
font-weight: 300;;
|
||||
|
||||
&:hover {
|
||||
color: var(--bs-primary);
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.recent-posts {
|
||||
ul li {
|
||||
// Ensure proper wrapping on small screens
|
||||
@media (max-width: 767px) {
|
||||
a {
|
||||
overflow-wrap: break-word;
|
||||
line-height: 1.4;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.post-date {
|
||||
display: block;
|
||||
font-size: 0.875rem;
|
||||
color: var(--bs-gray-900);
|
||||
}
|
||||
}
|
||||
|
||||
// Categories use the global tag styling for consistency
|
||||
}
|
||||
}
|
||||
|
||||
// Featured posts styling
|
||||
.featured-post-container {
|
||||
width: 100%;
|
||||
.p-4 {
|
||||
padding: 2rem !important;
|
||||
}
|
||||
|
||||
.col-lg-8 {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
|
||||
.p-md-5 {
|
||||
padding: 3rem !important;
|
||||
}
|
||||
|
||||
.col-lg-8 {
|
||||
max-width: 90%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Featured post thumbnail styling
|
||||
.featured-thumbnail {
|
||||
max-height: 300px;
|
||||
width: auto;
|
||||
object-fit: cover;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||
|
||||
@media (min-width: 992px) {
|
||||
max-height: 350px;
|
||||
}
|
||||
}
|
||||
|
||||
// Secondary featured posts image styling
|
||||
.secondary-featured-img {
|
||||
width: 200px;
|
||||
height: 250px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
// Table of Contents styling
|
||||
.table-of-contents {
|
||||
margin: 2rem 0;
|
||||
padding: 1.5rem;
|
||||
background: #f8f9fa;
|
||||
border-radius: 0.5rem;
|
||||
border-left: 4px solid var(--bs-primary);
|
||||
|
||||
.toc-heading {
|
||||
margin-bottom: 1rem;
|
||||
font-size: 1.2rem;
|
||||
font-weight: 600;
|
||||
color: var(--bs-gray-900);
|
||||
}
|
||||
|
||||
.toc-content {
|
||||
ul {
|
||||
list-style: none;
|
||||
padding-left: 0;
|
||||
margin: 0;
|
||||
|
||||
ul {
|
||||
padding-left: 1.5rem;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
li {
|
||||
margin-bottom: 0.5rem;
|
||||
|
||||
a {
|
||||
color: var(--bs-body-color);
|
||||
text-decoration: none;
|
||||
font-weight: 400;
|
||||
transition: color 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
color: var(--bs-primary);
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include color-mode(dark) {
|
||||
.blog {
|
||||
|
||||
.post .post-title a {
|
||||
color: $dark-primary;
|
||||
}
|
||||
|
||||
// Readability improvements for dark mode
|
||||
.post-summary,
|
||||
.post-content {
|
||||
color: var(--bs-gray-200);
|
||||
|
||||
code {
|
||||
background-color: var(--bs-gray-800);
|
||||
color: var(--bs-gray-200);
|
||||
}
|
||||
|
||||
pre {
|
||||
background-color: var(--bs-gray-800);
|
||||
}
|
||||
|
||||
blockquote {
|
||||
color: var(--bs-gray-400);
|
||||
}
|
||||
}
|
||||
|
||||
.blog-sidebar {
|
||||
.sidebar-section {
|
||||
background: var(--bs-gray-800);
|
||||
|
||||
h3 {
|
||||
color: var(--bs-white);
|
||||
}
|
||||
|
||||
/* Remove the general black color for all links */
|
||||
/* ul li a {
|
||||
color: black;
|
||||
} */
|
||||
|
||||
&.recent-posts {
|
||||
a {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.post-date {
|
||||
color: var(--bs-gray-500);
|
||||
}
|
||||
}
|
||||
|
||||
// Categories dark mode handled by global tag styling
|
||||
}
|
||||
}
|
||||
|
||||
// Table of Contents dark mode styling
|
||||
.table-of-contents {
|
||||
background: var(--bs-gray-800);
|
||||
|
||||
.toc-heading {
|
||||
color: var(--bs-white);
|
||||
}
|
||||
|
||||
.toc-content ul li a {
|
||||
color: var(--bs-gray-300);
|
||||
|
||||
&:hover {
|
||||
color: var(--bs-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.btn-outline-secondary{
|
||||
color: var(--bs-white);
|
||||
border-color: var(--bs-white);
|
||||
}
|
||||
}
|
||||
|
||||
.posts-list-simple {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
||||
li.post {
|
||||
padding: 1rem 0;
|
||||
border-bottom: 1px solid var(--bs-border-color);
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--bs-body-color);
|
||||
text-decoration: none;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 500;
|
||||
|
||||
&:hover {
|
||||
color: var(--bs-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.meta {
|
||||
font-size: 0.9rem;
|
||||
color: var(--bs-secondary-color);
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
.experience__company {
|
||||
color: $base-color;
|
||||
}
|
||||
|
||||
|
||||
/* enhanced contrast */
|
||||
@include color-mode(dark) {
|
||||
.experience__company {
|
||||
color: $dark-primary;
|
||||
}
|
||||
}
|
||||
|
||||
.experience__location {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.experience + .experience {
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
||||
body.home div.experience {
|
||||
padding-top: 50px;
|
||||
}
|
||||
|
||||
.experience a {
|
||||
display: block;
|
||||
padding: 20px;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.experience.selected a,
|
||||
.experience:hover a,
|
||||
.experience:active a,
|
||||
.experience a:hover,
|
||||
.experience a:active {
|
||||
display: block;
|
||||
background-color: $base-color;
|
||||
}
|
||||
|
||||
.experience.selected a .experience__company,
|
||||
.experience:hover a .experience__company,
|
||||
.experience:active a .experience__company,
|
||||
.experience a:hover .experience__company,
|
||||
.experience a:active .experience__company,
|
||||
.experience.selected a .experience__date,
|
||||
.experience:hover a .experience__date,
|
||||
.experience:active a .experience__date,
|
||||
.experience a:hover .experience__date,
|
||||
.experience a:active .experience__date {
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
|
||||
.all-experience-container {
|
||||
margin-top: 50px;
|
||||
}
|
||||
|
||||
.experience__title {
|
||||
position: relative;
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
.experience__title {
|
||||
font-size: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.experience__company {
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.experience {
|
||||
&__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
&__company-logo {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
object-fit: contain;
|
||||
border-radius: 4px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&__meta {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
&__date {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
&__title {
|
||||
font-weight: 600;
|
||||
margin: 0.2rem 0;
|
||||
}
|
||||
|
||||
&__company {
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
}
|
||||
|
||||
.job-card {
|
||||
padding: 1.5rem;
|
||||
border-radius: 8px;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
|
||||
|
||||
html[data-bs-theme="dark"] & {
|
||||
background-color: #2d2d2d;
|
||||
color: white;
|
||||
|
||||
.experience__title,
|
||||
.experience__company,
|
||||
.experience__date,
|
||||
.experience__location {
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.company-logo {
|
||||
object-fit: contain;
|
||||
}
|
||||
@@ -0,0 +1,307 @@
|
||||
// Related Posts Styling
|
||||
.related-posts {
|
||||
margin: 2rem 0;
|
||||
padding-top: 2rem;
|
||||
|
||||
h3 {
|
||||
margin-bottom: 1.5rem;
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
color: var(--bs-gray-900);
|
||||
}
|
||||
|
||||
.related-posts-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
display: grid;
|
||||
gap: 1.5rem;
|
||||
grid-template-columns: 1fr;
|
||||
|
||||
@media (min-width: 768px) {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
.related-post-item {
|
||||
background: var(--bs-gray-100);
|
||||
border-radius: 0.5rem;
|
||||
padding: 1.5rem;
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
display: block;
|
||||
|
||||
h4 {
|
||||
margin-bottom: 0.75rem;
|
||||
font-size: 1.125rem;
|
||||
font-weight: 600;
|
||||
color: var(--bs-primary);
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
&:hover h4 {
|
||||
color: var(--bs-link-hover-color);
|
||||
}
|
||||
}
|
||||
|
||||
.related-post-excerpt {
|
||||
font-size: 0.95rem;
|
||||
color: var(--bs-gray-700);
|
||||
margin-bottom: 1rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.related-post-meta {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
align-items: center;
|
||||
font-size: 0.875rem;
|
||||
|
||||
.post-date {
|
||||
color: var(--bs-gray-600);
|
||||
}
|
||||
|
||||
.post-tags {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.tag {
|
||||
background: var(--bs-primary);
|
||||
color: white;
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 0.25rem;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Social Sharing Styling
|
||||
.social-sharing {
|
||||
margin: 2rem 0;
|
||||
padding-top: 2rem;
|
||||
|
||||
h3 {
|
||||
margin-bottom: 1rem;
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
color: var(--bs-gray-900);
|
||||
}
|
||||
|
||||
.share-buttons {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.share-button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 0.375rem;
|
||||
text-decoration: none;
|
||||
font-size: 0.95rem;
|
||||
font-weight: 500;
|
||||
transition: all 0.2s ease;
|
||||
border: 1px solid transparent;
|
||||
|
||||
svg {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
&.share-twitter {
|
||||
background: #1DA1F2;
|
||||
color: white;
|
||||
border-color: #1DA1F2;
|
||||
|
||||
&:hover {
|
||||
background: #1a8cd8;
|
||||
border-color: #1a8cd8;
|
||||
}
|
||||
}
|
||||
|
||||
&.share-linkedin {
|
||||
background: #0077B5;
|
||||
color: white;
|
||||
border-color: #0077B5;
|
||||
|
||||
&:hover {
|
||||
background: #006399;
|
||||
border-color: #006399;
|
||||
}
|
||||
}
|
||||
|
||||
&.share-facebook {
|
||||
background: #1877F2;
|
||||
color: white;
|
||||
border-color: #1877F2;
|
||||
|
||||
&:hover {
|
||||
background: #155db2;
|
||||
border-color: #155db2;
|
||||
}
|
||||
}
|
||||
|
||||
&.share-email {
|
||||
background: var(--bs-gray-700);
|
||||
color: white;
|
||||
border-color: var(--bs-gray-700);
|
||||
|
||||
&:hover {
|
||||
background: var(--bs-gray-800);
|
||||
border-color: var(--bs-gray-800);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Table of Contents Styling
|
||||
.table-of-contents {
|
||||
margin: 2rem 0;
|
||||
padding: 1.5rem;
|
||||
background: var(--bs-gray-100);
|
||||
border-radius: 0.5rem;
|
||||
border-left: 4px solid var(--bs-primary);
|
||||
|
||||
&.toc-sticky {
|
||||
@media (min-width: 992px) {
|
||||
position: sticky;
|
||||
top: 2rem;
|
||||
max-height: calc(100vh - 4rem);
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin-bottom: 1rem;
|
||||
font-size: 1.2rem;
|
||||
font-weight: 600;
|
||||
color: var(--bs-gray-900);
|
||||
}
|
||||
|
||||
#TableOfContents {
|
||||
ul {
|
||||
list-style: none;
|
||||
padding-left: 0;
|
||||
margin: 0;
|
||||
|
||||
ul {
|
||||
padding-left: 1.5rem;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
li {
|
||||
margin-bottom: 0.5rem;
|
||||
|
||||
a {
|
||||
color: var(--bs-body-color);
|
||||
text-decoration: none;
|
||||
font-weight: 400;
|
||||
transition: color 0.2s ease;
|
||||
display: block;
|
||||
padding: 0.25rem 0;
|
||||
|
||||
&:hover {
|
||||
color: var(--bs-primary);
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Comments Section Styling
|
||||
.comments-section {
|
||||
margin: 2rem 0;
|
||||
padding-top: 2rem;
|
||||
|
||||
h3 {
|
||||
margin-bottom: 1.5rem;
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
color: var(--bs-gray-900);
|
||||
}
|
||||
}
|
||||
|
||||
// Dark Mode Adjustments
|
||||
@include color-mode(dark) {
|
||||
.related-posts {
|
||||
h3 {
|
||||
color: var(--bs-white);
|
||||
}
|
||||
|
||||
.related-post-item {
|
||||
background: var(--bs-gray-800);
|
||||
|
||||
a h4 {
|
||||
color: $dark-primary;
|
||||
}
|
||||
|
||||
.related-post-excerpt {
|
||||
color: var(--bs-gray-300);
|
||||
}
|
||||
|
||||
.related-post-meta {
|
||||
.post-date {
|
||||
color: var(--bs-gray-400);
|
||||
}
|
||||
|
||||
.post-tags .tag {
|
||||
background: $dark-primary;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.social-sharing {
|
||||
h3 {
|
||||
color: var(--bs-white);
|
||||
}
|
||||
}
|
||||
|
||||
.table-of-contents {
|
||||
background: var(--bs-gray-800);
|
||||
border-left-color: $dark-primary;
|
||||
|
||||
h3 {
|
||||
color: var(--bs-white);
|
||||
}
|
||||
|
||||
#TableOfContents ul li a {
|
||||
color: var(--bs-gray-300);
|
||||
|
||||
&:hover {
|
||||
color: $dark-primary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.comments-section {
|
||||
h3 {
|
||||
color: var(--bs-white);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
$color: white;
|
||||
$color-border: $base-color;
|
||||
$color-dark: scale-color($color, $lightness: -30%);
|
||||
$transition: 280ms all 120ms ease-out;
|
||||
|
||||
.underline-item {
|
||||
border-bottom: 1px solid $color;
|
||||
}
|
||||
|
||||
// prevents the menu content from not being visible
|
||||
// https://github.com/zetxek/adritian-free-hugo-theme/issues/206
|
||||
.header .navbar .nav-item.language-selector,
|
||||
.header .navbar .nav-item.theme-selector {
|
||||
overflow: visible;
|
||||
|
||||
.nav-link {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.language-selector,
|
||||
.theme-selector {
|
||||
li.choice {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// footer button
|
||||
.footer_right button.btn,
|
||||
.footer_right button.btn:active{
|
||||
color: white;
|
||||
}
|
||||
|
||||
// dropdown item links
|
||||
.footer_right .btn-link .dropdown-item {
|
||||
color: $color;
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
.bd-navbar {
|
||||
padding: .75rem 0;
|
||||
background-color: transparent;
|
||||
|
||||
@media (forced-colors) {
|
||||
background-color: Canvas;
|
||||
}
|
||||
|
||||
&::after {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: -1;
|
||||
display: block;
|
||||
content: "";
|
||||
background-image: linear-gradient(rgba(var(--bd-violet-rgb), 1), rgba(var(--bd-violet-rgb), .95));
|
||||
}
|
||||
|
||||
.bd-navbar-toggle {
|
||||
@include media-breakpoint-down(lg) {
|
||||
width: 4.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
.navbar-toggler {
|
||||
padding: 0;
|
||||
margin-right: -.5rem;
|
||||
border: 0;
|
||||
|
||||
&:first-child {
|
||||
margin-left: -.5rem;
|
||||
}
|
||||
|
||||
.bi {
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
color: $white;
|
||||
@include transition(transform .2s ease-in-out);
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.04);
|
||||
}
|
||||
}
|
||||
|
||||
.navbar-toggler,
|
||||
.nav-link {
|
||||
padding-right: $spacer * .25;
|
||||
padding-left: $spacer * .25;
|
||||
color: rgba($white, .85);
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
color: $white;
|
||||
}
|
||||
|
||||
&.active {
|
||||
font-weight: 600;
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
|
||||
.navbar-nav-svg {
|
||||
display: inline-block;
|
||||
vertical-align: -.125rem;
|
||||
}
|
||||
|
||||
.offcanvas-lg {
|
||||
background-color: var(--bd-violet-bg);
|
||||
border-left: 0;
|
||||
|
||||
@include media-breakpoint-down(lg) {
|
||||
box-shadow: var(--bs-box-shadow-lg);
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-toggle {
|
||||
&:focus:not(:focus-visible) {
|
||||
outline: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
--bs-dropdown-min-width: 12rem;
|
||||
--bs-dropdown-padding-x: .25rem;
|
||||
--bs-dropdown-padding-y: .25rem;
|
||||
--bs-dropdown-link-hover-bg: rgba(var(--bd-violet-rgb), .1);
|
||||
--bs-dropdown-link-active-bg: rgba(var(--bd-violet-rgb), 1);
|
||||
@include rfs(.875rem, --bs-dropdown-font-size);
|
||||
@include font-size(.875rem);
|
||||
@include border-radius(.5rem);
|
||||
box-shadow: $dropdown-box-shadow;
|
||||
|
||||
li + li {
|
||||
margin-top: .125rem;
|
||||
}
|
||||
|
||||
.dropdown-item {
|
||||
@include border-radius(.25rem);
|
||||
|
||||
&:active {
|
||||
.bi {
|
||||
color: inherit !important; // stylelint-disable-line declaration-no-important
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.active {
|
||||
font-weight: 600;
|
||||
|
||||
.bi {
|
||||
display: block !important; // stylelint-disable-line declaration-no-important
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-menu-end {
|
||||
--bs-dropdown-min-width: 8rem;
|
||||
}
|
||||
}
|
||||
|
||||
@include color-mode(dark) {
|
||||
.bd-navbar {
|
||||
box-shadow: 0 .5rem 1rem rgba($black, .15), inset 0 -1px 0 rgba($white, .15);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
.transition-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #fff;
|
||||
z-index: 9999;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
/* For dark mode */
|
||||
html[data-bs-theme="dark"] .transition-overlay {
|
||||
background-color: #181818;
|
||||
}
|
||||
|
||||
.transition-active .transition-overlay {
|
||||
opacity: 1;
|
||||
pointer-events: all;
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
@media print {
|
||||
|
||||
.bd-navbar {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
* {
|
||||
color: black !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
html[data-bs-theme="dark"] body.page-cv .h1,
|
||||
html[data-bs-theme="dark"] .h2,
|
||||
html[data-bs-theme="dark"] .h3,
|
||||
html[data-bs-theme="dark"] .h4,
|
||||
html[data-bs-theme="dark"] .h5,
|
||||
html[data-bs-theme="dark"] .h6,
|
||||
html[data-bs-theme="dark"] h1,
|
||||
html[data-bs-theme="dark"] h2,
|
||||
html[data-bs-theme="dark"] h3,
|
||||
html[data-bs-theme="dark"] h4,
|
||||
html[data-bs-theme="dark"] h5,
|
||||
html[data-bs-theme="dark"] h6,
|
||||
html[data-bs-theme="dark"] .h1,
|
||||
html[data-bs-theme="dark"] .h2,
|
||||
html[data-bs-theme="dark"] .h3,
|
||||
html[data-bs-theme="dark"] .h4,
|
||||
html[data-bs-theme="dark"] .h5,
|
||||
html[data-bs-theme="dark"] .h6,
|
||||
html[data-bs-theme="dark"] h1,
|
||||
html[data-bs-theme="dark"] h2,
|
||||
html[data-bs-theme="dark"] h3,
|
||||
html[data-bs-theme="dark"] h4,
|
||||
html[data-bs-theme="dark"] h5,
|
||||
html[data-bs-theme="dark"] h6 {
|
||||
color: black !important;
|
||||
}
|
||||
|
||||
.experience__meta {
|
||||
flex: 0 0 200px;
|
||||
}
|
||||
|
||||
.experience__description {
|
||||
flex: 1 1 400px;
|
||||
}
|
||||
|
||||
.experience__list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
page-break-after: avoid;
|
||||
break-after: avoid;
|
||||
}
|
||||
|
||||
p,
|
||||
section {
|
||||
page-break-after: avoid;
|
||||
break-after: avoid;
|
||||
break-before: avoid;
|
||||
}
|
||||
|
||||
p {
|
||||
break-inside: avoid;
|
||||
}
|
||||
|
||||
header {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.section-experience {
|
||||
// Remove decorative elements
|
||||
background: none !important;
|
||||
box-shadow: none !important;
|
||||
border: none !important;
|
||||
|
||||
// Ensure black text on white background
|
||||
color: #000 !important;
|
||||
|
||||
// Adjust spacing
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
|
||||
// Make sure content is visible
|
||||
display: block !important;
|
||||
page-break-inside: avoid;
|
||||
|
||||
.experience {
|
||||
width: 100%;
|
||||
// Improve experience item layout for print
|
||||
page-break-inside: avoid;
|
||||
border: none !important;
|
||||
padding: 0.5cm 0 !important;
|
||||
|
||||
a {
|
||||
// Remove link styling
|
||||
color: inherit !important;
|
||||
text-decoration: none !important;
|
||||
padding: 0 !important;
|
||||
background: none !important;
|
||||
}
|
||||
|
||||
&__company-logo {
|
||||
// Ensure logos print well
|
||||
print-color-adjust: exact;
|
||||
-webkit-print-color-adjust: exact;
|
||||
}
|
||||
|
||||
&__meta {
|
||||
// Adjust text for better print readability
|
||||
color: #000 !important;
|
||||
}
|
||||
|
||||
&__company,
|
||||
&__location,
|
||||
&__date,
|
||||
&__title {
|
||||
color: #000 !important;
|
||||
}
|
||||
}
|
||||
|
||||
// Hide interactive elements
|
||||
.btn,
|
||||
.all-experience-container {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure job card prints well
|
||||
.job-card {
|
||||
background: none !important;
|
||||
box-shadow: none !important;
|
||||
border: 1px solid #ddd !important;
|
||||
padding: 1cm !important;
|
||||
margin-bottom: 1cm !important;
|
||||
|
||||
.company-logo {
|
||||
print-color-adjust: exact;
|
||||
-webkit-print-color-adjust: exact;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
color: black !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
// Search page styles
|
||||
.search-section {
|
||||
padding: 4rem 0;
|
||||
}
|
||||
|
||||
#search-results {
|
||||
margin-top: 2rem;
|
||||
|
||||
.card-body {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
// Style for the "Please enter a word or phrase above" message
|
||||
.alert {
|
||||
margin: 1.5rem;
|
||||
padding: 1rem;
|
||||
border: 1px solid #cce5ff;
|
||||
border-radius: 0.25rem;
|
||||
background-color: #e6f2ff;
|
||||
color: #004085;
|
||||
}
|
||||
}
|
||||
|
||||
// Style for the search form
|
||||
#search-query {
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
.input-group-text {
|
||||
cursor: pointer;
|
||||
background-color: $base-color;
|
||||
color: white;
|
||||
border-color: $base-color;
|
||||
|
||||
&:hover {
|
||||
background-color: darken($base-color, 10%);
|
||||
}
|
||||
}
|
||||
|
||||
// Search result item styling
|
||||
#summary-template {
|
||||
padding: 1rem 0;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
// Dark mode overrides
|
||||
@include color-mode(dark) {
|
||||
|
||||
#main-content.search-section {
|
||||
background-color: #181818 !important;
|
||||
color: #f8f9fa;
|
||||
|
||||
h2, h3 {
|
||||
color: #f8f9fa;
|
||||
}
|
||||
}
|
||||
|
||||
#search-query {
|
||||
background-color: #222222;
|
||||
border-color: #333333;
|
||||
color: #f8f9fa;
|
||||
|
||||
&::placeholder {
|
||||
color: #8a8a8a;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
box-shadow: 0 0 0 0.25rem rgba($base-color, 0.25);
|
||||
border-color: $base-color;
|
||||
}
|
||||
}
|
||||
|
||||
.input-group-text {
|
||||
background-color: $base-color;
|
||||
color: white;
|
||||
border-color: $base-color;
|
||||
}
|
||||
|
||||
#search-results {
|
||||
background-color: #222222;
|
||||
|
||||
.card-body {
|
||||
background-color: #222222;
|
||||
border-color: #333333;
|
||||
}
|
||||
|
||||
.alert {
|
||||
background-color: transparent;
|
||||
border-color: transparent;
|
||||
color: #e0e0e0;
|
||||
margin: 0;
|
||||
padding: 1.5rem;
|
||||
border-radius: 0;
|
||||
border-width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.border-bottom {
|
||||
border-color: #333 !important;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
.skill-bar {
|
||||
height: 10px;
|
||||
background-color: #e9ecef;
|
||||
border-radius: 5px;
|
||||
margin-bottom: 5px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.skill-bar-fill {
|
||||
height: 100%;
|
||||
background-color: $primary;
|
||||
border-radius: 5px;
|
||||
transition: width 1s ease-in-out;
|
||||
}
|
||||
|
||||
.skill-item {
|
||||
margin-bottom: 30px;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
background-color: #f8f9fa;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.skill-item:hover {
|
||||
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
|
||||
.skill-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.skill-name {
|
||||
font-weight: 600;
|
||||
font-size: 1.1rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.skill-level {
|
||||
font-size: 0.9rem;
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
.skill-years {
|
||||
display: inline-block;
|
||||
padding: 3px 8px;
|
||||
background-color: #e9ecef;
|
||||
border-radius: 20px;
|
||||
font-size: 0.8rem;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.skill-description {
|
||||
font-size: 0.9rem;
|
||||
color: #6c757d;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.site-skills-section {
|
||||
padding: 100px 0;
|
||||
}
|
||||
|
||||
.site-skills-title {
|
||||
margin-bottom: 20px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.site-skills-description {
|
||||
max-width: 800px;
|
||||
margin: 0 auto 50px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.site-skills-section {
|
||||
padding: 60px 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Dark mode support */
|
||||
@include color-mode(dark) {
|
||||
.skill-item {
|
||||
background-color: #2d2d2d;
|
||||
}
|
||||
|
||||
.skill-name {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.skill-level, .skill-description {
|
||||
color: #adb5bd;
|
||||
}
|
||||
|
||||
.skill-years {
|
||||
background-color: #444;
|
||||
color: #ddd;
|
||||
}
|
||||
|
||||
.skill-bar {
|
||||
background-color: #444;
|
||||
}
|
||||
|
||||
.skill-bar-fill {
|
||||
background-color: $dark-primary;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/* Preload styles to prevent theme flash */
|
||||
:root {
|
||||
&[data-bs-theme=dark] {
|
||||
color-scheme: dark;
|
||||
--bs-body-color: #fff;
|
||||
--bs-body-bg: #181818;
|
||||
}
|
||||
|
||||
&[data-bs-theme=light] {
|
||||
color-scheme: light;
|
||||
--bs-body-color: #000;
|
||||
--bs-body-bg: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
/* Ensure smooth transition when switching themes */
|
||||
html {
|
||||
transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
/* Prevent flicker on elements that change color with theme */
|
||||
body, p, h1, h2, h3, h4, h5, h6, a {
|
||||
transition: color 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
/* Applied during theme changes to ensure smooth transitions */
|
||||
html.theme-transition,
|
||||
html.theme-transition *,
|
||||
html.theme-transition *:before,
|
||||
html.theme-transition *:after {
|
||||
transition: all 0s !important;
|
||||
-webkit-transition: all 0s !important;
|
||||
}
|
||||
|
||||
/* Specific auto mode handling - prevents flash when system preference changes */
|
||||
html[data-theme-auto="true"] {
|
||||
transition: background-color 0.3s ease-in-out, color 0.3s ease-in-out;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// Base colors
|
||||
$base-color: #478079; // #478079 - Your theme's primary color
|
||||
$primary: $base-color;
|
||||
$dark-primary: #66b2a9; // Brighter primary color for dark mode - enhanced contrast
|
||||
|
||||
// Link colors
|
||||
$link-color: $base-color;
|
||||
$link-hover-color: darken($base-color, 15%);
|
||||
$link-decoration: none;
|
||||
$sidebar-link-color: #000; // increased contrast
|
||||
|
||||
// Create CSS custom properties for Bootstrap
|
||||
:root {
|
||||
--bs-link-color: #{$base-color};
|
||||
--bs-link-color-rgb: #{red($base-color)}, #{green($base-color)}, #{blue($base-color)};
|
||||
--bs-link-hover-color: #{darken($base-color, 15%)};
|
||||
--bs-link-hover-color-rgb: #{red(darken($base-color, 15%))}, #{green(darken($base-color, 15%))}, #{blue(darken($base-color, 15%))};
|
||||
--bs-link-decoration: none;
|
||||
}
|
||||
|
||||
// Explicit link override for all <a> tags
|
||||
a {
|
||||
color: $base-color;
|
||||
text-decoration: $link-decoration;
|
||||
|
||||
&:hover {
|
||||
color: $link-hover-color;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,565 @@
|
||||
@charset "UTF-8";
|
||||
|
||||
@import "variables";
|
||||
@import "theme-init";
|
||||
@import "bootstrap/bootstrap";
|
||||
@import "navbar";
|
||||
@import "menu";
|
||||
@import "raditian";
|
||||
@import "blog";
|
||||
@import "experience";
|
||||
@import "print";
|
||||
@import "search";
|
||||
@import "page-transition";
|
||||
@import "skills";
|
||||
@import "features";
|
||||
|
||||
/** main style **/
|
||||
.header .navbar-brand span:first-child {
|
||||
color: $base-color;
|
||||
}
|
||||
|
||||
.header .container {
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
html body ::selection {
|
||||
background-color: $base-color;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding-left: 24px;
|
||||
padding-right: 24px;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding-left: 24px;
|
||||
padding-right: 24px;
|
||||
}
|
||||
|
||||
.h1,
|
||||
.h2,
|
||||
.h3,
|
||||
.h4,
|
||||
.h5,
|
||||
.h6,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-weight: 700;
|
||||
line-height: normal;
|
||||
color: #000;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.btn:focus-visible{
|
||||
box-shadow: 0 0 0 0.2rem $primary;
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
.h1,
|
||||
.h2,
|
||||
.h3,
|
||||
.h4,
|
||||
.h5,
|
||||
.h6,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
h1,
|
||||
h2 {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
h1 {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
h2 {
|
||||
margin-bottom: 10px;
|
||||
font-size: 36px;
|
||||
}
|
||||
}
|
||||
|
||||
h3,
|
||||
h4 {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
h3,
|
||||
h4 {
|
||||
font-size: 32px;
|
||||
}
|
||||
p {
|
||||
line-height: 28px;
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 16px;
|
||||
font-weight: 300;
|
||||
line-height: 28px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
p + p {
|
||||
margin-top: -12px;
|
||||
}
|
||||
|
||||
b, strong{
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.display-1 {
|
||||
font-size: 36px;
|
||||
font-weight: 800;
|
||||
line-height: normal;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.showcase-section .platform-links.shortcode {
|
||||
/* align to the left margin */
|
||||
margin-left: -40px;
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
.display-1 {
|
||||
font-size: 60px;
|
||||
}
|
||||
}
|
||||
|
||||
.lead {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.lead a,
|
||||
p a,
|
||||
span a {
|
||||
color: #000;
|
||||
font-weight: 600;
|
||||
text-decoration: underline;
|
||||
text-underline-position: from-font;
|
||||
text-decoration-style: solid;
|
||||
text-decoration-thickness: 1px;
|
||||
transition: all 0.5s;
|
||||
}
|
||||
|
||||
.lead a:hover,
|
||||
p a:hover,
|
||||
span a:hover {
|
||||
color: $base-color;
|
||||
}
|
||||
|
||||
.lead {
|
||||
font-size: 16px;
|
||||
font-weight: 300;
|
||||
line-height: 1.2;
|
||||
letter-spacing: -0px;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
br {
|
||||
content: "";
|
||||
margin: 2em;
|
||||
display: block;
|
||||
font-size: 24%;
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
.lead {
|
||||
font-size: 18px;
|
||||
}
|
||||
.contact {
|
||||
margin-top: -2px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
.ml-lg-auto,
|
||||
.mx-lg-auto {
|
||||
margin-left: auto !important;
|
||||
}
|
||||
|
||||
#mobile-header-language-selector,
|
||||
#mobile-header-color-selector {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-item.selected {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#mobile-header-language-selector,
|
||||
#mobile-header-color-selector {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#languages-dropdown-mobile-header,
|
||||
#theme-dropdown-mobile-header {
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
a.dropdown-item.translation,
|
||||
li.dropdown-item.current.selected {
|
||||
width: 100%;
|
||||
padding: 4px 10px;
|
||||
}
|
||||
|
||||
li.dropdown-item.choice a {
|
||||
padding-left: 30px;
|
||||
}
|
||||
|
||||
.dropdown-menu button[aria-pressed="true"] {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
html[data-bs-theme="dark"] {
|
||||
#dark-toggle {
|
||||
span.dark {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
html[data-bs-theme="light"] {
|
||||
#dark-toggle {
|
||||
span.light {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
footer_links .nav-item .nav-link::after {
|
||||
color: white;
|
||||
background-color: white !important;
|
||||
}
|
||||
|
||||
/* skip to content */
|
||||
.skip-to-content-link {
|
||||
position: absolute;
|
||||
left: -9999px;
|
||||
z-index: 999;
|
||||
padding: 1em;
|
||||
background-color: white;
|
||||
color: black;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.skip-to-content-link:focus {
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* dark mode */
|
||||
@include color-mode(dark) {
|
||||
$primary: $dark-primary;
|
||||
html body,
|
||||
body {
|
||||
color: #fff;
|
||||
background-color: #181818;
|
||||
}
|
||||
.header .navbar-brand span:nth-child(2) {
|
||||
color: #fff;
|
||||
}
|
||||
.header .navbar .nav-item .nav-link::after {
|
||||
background-color: #fff;
|
||||
}
|
||||
.header .navbar .nav-item .nav-link:hover a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.navbar-light .navbar-toggler-icon {
|
||||
background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3e%3cpath stroke='rgba(255, 255, 255, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e") !important;
|
||||
}
|
||||
.header.collapse.show::before,
|
||||
.header.collapsing::before {
|
||||
background-color: #181818;
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
.header .navbar .nav-item:hover .nav-link::after {
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 990px) {
|
||||
.header .navbar .nav-item:first-child {
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
.header .navbar .nav-item {
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
}
|
||||
.header--sticky-triggered {
|
||||
background: #181818;
|
||||
}
|
||||
.header .navbar-light .navbar-nav .nav-link,
|
||||
.header .navbar-light .navbar-nav .btn-link {
|
||||
color: #fff;
|
||||
}
|
||||
.h1,
|
||||
.h2,
|
||||
.h3,
|
||||
.h4,
|
||||
.h5,
|
||||
.h6,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
color: #fff;
|
||||
}
|
||||
.section li::before {
|
||||
background-color: $light-text-emphasis-dark;
|
||||
}
|
||||
.display-1 {
|
||||
color: #fff;
|
||||
}
|
||||
.lead {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.lead a,
|
||||
p a,
|
||||
span a {
|
||||
color: #fff;
|
||||
}
|
||||
.lead a:hover,
|
||||
p a:hover,
|
||||
span a:hover {
|
||||
color: $dark-primary;
|
||||
}
|
||||
p {
|
||||
color: $light-text-emphasis-dark;
|
||||
}
|
||||
.platform-links [class^="icon-"] {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.education__degree,
|
||||
.experience__date {
|
||||
color: #a6a6a6;
|
||||
}
|
||||
.education__title {
|
||||
color: #fff;
|
||||
}
|
||||
.education__date {
|
||||
color: #d5d5d5;
|
||||
}
|
||||
.education::before {
|
||||
background-color: #202020;
|
||||
}
|
||||
.testimonial__author-info span {
|
||||
color: #f18983;
|
||||
}
|
||||
.section--contact .container {
|
||||
background-color: #181818;
|
||||
}
|
||||
.contact__info span {
|
||||
color: #fff;
|
||||
}
|
||||
.contact__info h3 {
|
||||
color: #bebdbd;
|
||||
}
|
||||
.section--contact {
|
||||
background-image: url(../img/contact-bg-dark.png);
|
||||
background-color: #181818;
|
||||
}
|
||||
.btn-primary {
|
||||
background-color: #fff;
|
||||
color: #000;
|
||||
}
|
||||
.btn-primary:hover {
|
||||
border-color: #fff;
|
||||
color: #fff;
|
||||
background: transparent;
|
||||
}
|
||||
.btn-frameless {
|
||||
color: #fff;
|
||||
border-color: #fff;
|
||||
}
|
||||
.btn:focus-visible {
|
||||
color: black;
|
||||
background-color: white;
|
||||
box-shadow: 0 0 0 0.2rem $primary;
|
||||
}
|
||||
.btn-frameless.disabled,
|
||||
.btn-frameless.focus,
|
||||
.btn-frameless:disabled,
|
||||
.btn-frameless:focus,
|
||||
.btn-frameless:hover,
|
||||
.btn-frameless:not(:disabled):not(.disabled).active,
|
||||
.btn-frameless:not(:disabled):not(.disabled):active,
|
||||
.show > .btn-frameless.dropdown-toggle {
|
||||
background-color: white;
|
||||
border-color: #fff;
|
||||
color: #181818;
|
||||
}
|
||||
input.form-control,
|
||||
input[type="text"],
|
||||
input[type="email"],
|
||||
input[type="tel"],
|
||||
|
||||
textarea {
|
||||
color: #fff;
|
||||
border-bottom: 1px solid #fff;
|
||||
background-color: #181818;
|
||||
}
|
||||
input[type="text"]::placeholder,
|
||||
input[type="email"]::placeholder,
|
||||
textarea::placeholder {
|
||||
color: #6e6e6e;
|
||||
}
|
||||
input[type="text"]:focus,
|
||||
input[type="text"]:active,
|
||||
input[type="email"]:focus,
|
||||
input[type="email"]:active,
|
||||
textarea:focus,
|
||||
textarea:active {
|
||||
border-bottom: 2px solid #fff;
|
||||
}
|
||||
.rad-subscription-group input,
|
||||
.rad-subscription-group textarea {
|
||||
border: none;
|
||||
background: none;
|
||||
}
|
||||
.rad-subscription-group input[type="text"]:focus,
|
||||
.rad-subscription-group input[type="email"]:focus,
|
||||
.rad-subscription-group input[type="password"]:focus,
|
||||
.rad-subscription-group textarea:focus {
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.posts-list article.summary {
|
||||
color: #c3c3c3;
|
||||
}
|
||||
.post-summary.post-content p,
|
||||
.post-summary.post-content * {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.text-muted {
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
|
||||
.dropdown-item.active,
|
||||
.dropdown-item:active,
|
||||
.dropdown-item:focus,
|
||||
.dropdown-item:hover {
|
||||
background-color: $base-color;
|
||||
}
|
||||
|
||||
// Blog post meta section dark mode
|
||||
#blog-single #meta {
|
||||
#topics,
|
||||
.tags {
|
||||
li a {
|
||||
background-color: #2a2a2a;
|
||||
color: #e0e0e0;
|
||||
border: none;
|
||||
|
||||
&:hover {
|
||||
background-color: #3a3a3a;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// General tags styling for dark mode (consistency across all pages)
|
||||
ul.tags,
|
||||
ul.topics,
|
||||
#topics,
|
||||
ul.list-taxonomy {
|
||||
li a {
|
||||
background-color: rgba(255, 255, 255, 0.08);
|
||||
color: #e8e8e8;
|
||||
border: 1px solid rgba(255, 255, 255, 0.12);
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(255, 255, 255, 0.15);
|
||||
color: #fff;
|
||||
border-color: rgba(255, 255, 255, 0.2);
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.4), 0 0 12px rgba(255, 255, 255, 0.1);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Blog sidebar dark mode improvements
|
||||
.blog-sidebar {
|
||||
.sidebar-section {
|
||||
background: linear-gradient(135deg, rgba(42, 42, 42, 0.6) 0%, rgba(35, 35, 35, 0.7) 100%);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(255, 255, 255, 0.05);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
backdrop-filter: blur(10px);
|
||||
|
||||
h3 {
|
||||
color: #fff;
|
||||
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
|
||||
border-bottom: 2px solid rgba(255, 255, 255, 0.15);
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
ul.tags li a {
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
color: #e8e8e8;
|
||||
border: 1px solid rgba(255, 255, 255, 0.12);
|
||||
font-weight: 400;
|
||||
|
||||
&:hover {
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
color: #fff;
|
||||
border-color: rgba(255, 255, 255, 0.2);
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
&.recent-posts {
|
||||
ul li {
|
||||
a {
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
|
||||
&:hover {
|
||||
color: #fff;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.post-date {
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* end dark mode */
|
||||
@@ -0,0 +1,24 @@
|
||||
[module]
|
||||
[module.hugoVersion]
|
||||
extended = true
|
||||
[[module.mounts]]
|
||||
|
||||
# Workaround for https://github.com/gohugoio/hugo/issues/6945
|
||||
# source = "assets/scss/bootstrap/_vendor"
|
||||
# target = "assets/scss/bootstrap/vendor"
|
||||
[[module.mounts]]
|
||||
source = "assets"
|
||||
target = "assets"
|
||||
|
||||
## Commented as per https://github.com/zetxek/adritian-free-hugo-theme/discussions/158#discussioncomment-11916561
|
||||
# [[module.imports]]
|
||||
# ignoreConfig = true
|
||||
# path = "github.com/twbs/bootstrap"
|
||||
# [[module.imports.mounts]]
|
||||
# source = "scss"
|
||||
# target = "assets/scss/bootstrap"
|
||||
# [[module.imports.mounts]]
|
||||
# source = "js"
|
||||
# target = "assets/js/bootstrap"
|
||||
# [[module.imports]]
|
||||
# path = "github.com/gohugoio/hugo-mod-jslibs-dist/popperjs/v2"
|
||||
@@ -0,0 +1,51 @@
|
||||
---
|
||||
title: "Search Results"
|
||||
sitemap:
|
||||
priority : 0.1
|
||||
layout: "search"
|
||||
---
|
||||
|
||||
|
||||
This file exists solely to respond to /search URL with the related `search` layout template.
|
||||
|
||||
No content shown here is rendered, all content is based in the template layouts/page/search.html
|
||||
|
||||
Setting a very low sitemap priority will tell search engines this is not important content.
|
||||
|
||||
This implementation uses Fusejs, jquery and mark.js
|
||||
|
||||
|
||||
## Initial setup
|
||||
|
||||
Search depends on additional output content type of JSON in config.toml
|
||||
\```
|
||||
[outputs]
|
||||
home = ["HTML", "JSON"]
|
||||
\```
|
||||
|
||||
## Searching additional fileds
|
||||
|
||||
To search additional fields defined in front matter, you must add it in 2 places.
|
||||
|
||||
### Edit layouts/_default/index.JSON
|
||||
This exposes the values in /index.json
|
||||
i.e. add `category`
|
||||
\```
|
||||
...
|
||||
"contents":{{ .Content | plainify | jsonify }}
|
||||
{{ if .Params.tags }},
|
||||
"tags":{{ .Params.tags | jsonify }}{{end}},
|
||||
"categories" : {{ .Params.categories | jsonify }},
|
||||
...
|
||||
\```
|
||||
|
||||
### Edit fuse.js options to Search
|
||||
`static/js/search.js`
|
||||
\```
|
||||
keys: [
|
||||
"title",
|
||||
"contents",
|
||||
"tags",
|
||||
"categories"
|
||||
]
|
||||
\```
|
||||
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 8.2 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 9.9 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 9.8 KiB |
|
After Width: | Height: | Size: 8.1 KiB |
|
After Width: | Height: | Size: 55 KiB |
@@ -0,0 +1 @@
|
||||
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Hugo</title><path d="M11.754 0a3.998 3.998 0 00-2.049.596L3.33 4.532a4.252 4.252 0 00-2.017 3.615v8.03c0 1.473.79 2.838 2.067 3.574l6.486 3.733a3.88 3.88 0 003.835.018l7.043-3.966a3.817 3.817 0 001.943-3.323V7.752a3.57 3.57 0 00-1.774-3.084L13.817.541a3.998 3.998 0 00-2.063-.54zm.022 1.674c.413-.006.828.1 1.2.315l7.095 4.127c.584.34.941.96.94 1.635v8.462c0 .774-.414 1.484-1.089 1.864l-7.042 3.966a2.199 2.199 0 01-2.179-.01l-6.485-3.734a2.447 2.447 0 01-1.228-2.123v-8.03c0-.893.461-1.72 1.221-2.19l6.376-3.935a2.323 2.323 0 011.19-.347zm-4.7 3.844V18.37h2.69v-5.62h4.46v5.62h2.696V5.518h-2.696v4.681h-4.46V5.518Z"/></svg>
|
||||
|
After Width: | Height: | Size: 703 B |
|
After Width: | Height: | Size: 344 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 301 KiB |
|
After Width: | Height: | Size: 107 KiB |
|
After Width: | Height: | Size: 76 KiB |
|
After Width: | Height: | Size: 153 KiB |
@@ -0,0 +1,10 @@
|
||||
---
|
||||
title: "Articles"
|
||||
date: 2024-01-01
|
||||
draft: false
|
||||
description: "In-depth articles and tutorials"
|
||||
---
|
||||
|
||||
Welcome to the articles section. Here you'll find comprehensive articles, tutorials, and deep-dive content on various topics related to web development, technology, and best practices.
|
||||
|
||||
This section demonstrates how Hugo automatically creates sections from content folders, making it easy to organize different types of content.
|
||||
@@ -0,0 +1,40 @@
|
||||
---
|
||||
title: 'First Article'
|
||||
date: 2024-01-15T10:00:00+00:00
|
||||
draft: false
|
||||
tags:
|
||||
- tutorial
|
||||
- beginner
|
||||
---
|
||||
|
||||
This is the first article content. Hugo sections allow you to organize content into logical groups, and each section gets its own URL path.
|
||||
|
||||
## What are Hugo Sections?
|
||||
|
||||
Hugo sections are a way to organize your content into logical groups. When you create a folder under `content/`, Hugo automatically treats it as a section.
|
||||
|
||||
## Benefits of Using Sections
|
||||
|
||||
- **Organization**: Keep related content together
|
||||
- **URLs**: Automatic URL structure (`/articles/`, `/news/`, etc.)
|
||||
- **Templates**: Custom layouts for different content types
|
||||
- **RSS**: Each section gets its own RSS feed
|
||||
|
||||
## Example Structure
|
||||
|
||||
```
|
||||
content/
|
||||
├── articles/
|
||||
│ ├── _index.md
|
||||
│ ├── first-article.md
|
||||
│ └── second-article.md
|
||||
└── news/
|
||||
├── _index.md
|
||||
└── breaking-news.md
|
||||
```
|
||||
|
||||
This creates accessible URLs like:
|
||||
- `/articles/` (section list)
|
||||
- `/articles/first-article/` (individual article)
|
||||
- `/news/` (section list)
|
||||
- `/news/breaking-news/` (individual news item)
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
title: 'Advanced Hugo Sections'
|
||||
date: 2024-01-20T14:30:00+00:00
|
||||
draft: false
|
||||
tags:
|
||||
- hugo
|
||||
- advanced
|
||||
- sections
|
||||
---
|
||||
|
||||
This is the second article, demonstrating how multiple articles appear in the same section.
|
||||
|
||||
## Advanced Section Features
|
||||
|
||||
Hugo sections provide powerful features for content organization:
|
||||
|
||||
### Custom Section Templates
|
||||
|
||||
You can create custom templates for each section by placing them in `layouts/SECTION/`:
|
||||
|
||||
- `layouts/articles/list.html` - for `/articles/` page
|
||||
- `layouts/articles/single.html` - for individual articles
|
||||
|
||||
### Section Variables
|
||||
|
||||
In templates, you can access section-specific data:
|
||||
|
||||
```go
|
||||
{{ .Section }} // "articles"
|
||||
{{ .Type }} // content type
|
||||
{{ .Pages }} // all pages in section
|
||||
{{ .RegularPages }} // only regular pages
|
||||
```
|
||||
|
||||
### Taxonomies per Section
|
||||
|
||||
Each section can have its own taxonomies (tags, categories) with independent organization.
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
title: "Blog de Demostración"
|
||||
date: 2023-01-01
|
||||
draft: false
|
||||
---
|
||||
|
||||
Bienvenido al blog de demostración. Puedes personalizar (o eliminar) esta sección en el archivo `_index.md` de la carpeta `blog`. Puedes [leer sobre cómo organizar contenido en Hugo](https://gohugo.io/content-management/page-bundles/) en la documentación oficial.
|
||||
|
||||
Los artículos a continuación se encuentran en la misma carpeta (`blog/`) y pueden estar en [cualesquiera de los formatos que Hugo soporta](https://gohugo.io/content-management/formats/) (Markdown, HTML, Emacs Org Mode, AsciiDoc, Pandoc o reStructuredText).
|
||||
@@ -0,0 +1,11 @@
|
||||
---
|
||||
title: "Demo Blog"
|
||||
date: 2023-01-01
|
||||
draft: false
|
||||
---
|
||||
|
||||
Welcome to the demo blog. You can customize (or remove) this section in the `_index.md` file in the `blog` folder. You can [read about how to organize content in Hugo](https://gohugo.io/content-management/page-bundles/) in the official docs.
|
||||
|
||||
The posts below are in the same folder (`blog/`) and can be in the [supported formats by hugo](https://gohugo.io/content-management/formats/) (Markdown, HTML, Emacs Org Mode, AsciiDoc, Pandoc, or reStructuredText).
|
||||
|
||||
You can use translations as well.
|
||||
@@ -0,0 +1,396 @@
|
||||
---
|
||||
title: 'Create your own version of the site'
|
||||
date: 2025-02-11T14:38:33+02:00
|
||||
draft: false
|
||||
type: 'blog'
|
||||
featured: true
|
||||
weight: 10
|
||||
tags:
|
||||
- adritian
|
||||
- guide
|
||||
images:
|
||||
featured_image: '/img/blog/A minimalistic representation of creating a personal website, focusing on clean lines, subtle geometric shapes, and a modern web development aesthetic.png'
|
||||
---
|
||||
|
||||
This article is a guide to help you create your own version of the site using [Adritian](https://github.com/zetxek/adritian-free-hugo-theme). It will cover the main steps to get started with the theme, and how to customize it to your needs.
|
||||
|
||||
### Creating a site
|
||||
|
||||
This theme is for the content management system [Hugo](https://gohugo.io/), so that will be a pre-requirement.
|
||||
Make sure that you install the `extended` version of Hugo, as the theme uses SCSS for styling, as well as image optimization.
|
||||
|
||||
A very good place to start is the Quick start guide: [https://gohugo.io/getting-started/quick-start/](https://gohugo.io/getting-started/quick-start/)
|
||||
|
||||
_💡Tip:__ keep your repository clean and tidy by creating a [relevant `.gitignore` file](https://github.com/github/gitignore/blob/main/community/Golang/Hugo.gitignore) since the beginning:
|
||||
|
||||
```
|
||||
# Generated files by hugo
|
||||
/public/
|
||||
/resources/_gen/
|
||||
/assets/jsconfig.json
|
||||
hugo_stats.json
|
||||
|
||||
# Executable may be added to repository
|
||||
hugo.exe
|
||||
hugo.darwin
|
||||
hugo.linux
|
||||
|
||||
# Temporary lock file while building
|
||||
/.hugo_build.lock
|
||||
```
|
||||
|
||||
|
||||
|
||||
### Adding the theme
|
||||
|
||||
Once you have a site created, you can add the theme to your site by following the instructions in the [README](https://github.com/zetxek/adritian-free-hugo-theme?tab=readme-ov-file#as-a-hugo-module-recommended). While there are multiple ways to add the theme, the recommended way is to use the Hugo Modules, the ones listed here.
|
||||
|
||||
<details>
|
||||
<summary>Instructions to setup the theme as a hugo module</summary>
|
||||
|
||||
1. Create a new Hugo site (this will create a new folder): `hugo new site <your website's name>`
|
||||
1. Enter the newly created folder: `cd <your website's name>/`
|
||||
1. Initialize the Hugo Module system in your site if you haven't already: `hugo mod init github.com/username/your-site` (_you don't need to host your website on GitHub, you can add anything as a name_)
|
||||
1. Replace the contents of your config file (`hugo.toml`) file by these:
|
||||
|
||||
|
||||
<details>
|
||||
<summary>hugo.toml configuration</summary>
|
||||
|
||||
```
|
||||
baseURL = "<your website url>"
|
||||
languageCode = "en"
|
||||
|
||||
[module]
|
||||
[module.hugoVersion]
|
||||
# We use hugo.Deps to list dependencies, which was added in Hugo 0.92.0
|
||||
min = "0.92.0"
|
||||
|
||||
[[module.imports]]
|
||||
path="github.com/zetxek/adritian-free-hugo-theme"
|
||||
|
||||
## Base mounts - so your site's assets are available
|
||||
[[module.mounts]]
|
||||
source = "archetypes"
|
||||
target = "archetypes"
|
||||
|
||||
[[module.mounts]]
|
||||
source = "assets"
|
||||
target = "assets"
|
||||
|
||||
[[module.mounts]]
|
||||
source = "i18n"
|
||||
target = "i18n"
|
||||
|
||||
[[module.mounts]]
|
||||
source = "layouts"
|
||||
target = "layouts"
|
||||
|
||||
[[module.mounts]]
|
||||
source = "static"
|
||||
target = "static"
|
||||
|
||||
# The following mounts are required for the theme to be able to load bootstrap
|
||||
# Remember also to copy the theme's `package.json` to your site, and run `npm install`
|
||||
[[module.mounts]]
|
||||
source = "node_modules/bootstrap/scss"
|
||||
target = "assets/scss/bootstrap"
|
||||
|
||||
[[module.mounts]]
|
||||
source = "node_modules/bootstrap/dist/js"
|
||||
target = "assets/js/bootstrap"
|
||||
|
||||
[[module.mounts]]
|
||||
source = "node_modules/bootstrap/dist/js/bootstrap.bundle.min.js"
|
||||
target = "assets/js/vendor/bootstrap.bundle.min.js"
|
||||
|
||||
## Optional, if you want print improvements (to PDF/printed)
|
||||
[[module.mounts]]
|
||||
source = "node_modules/bootstrap-print-css/css/bootstrap-print.css"
|
||||
target = "assets/css/bootstrap-print.css"
|
||||
|
||||
[params]
|
||||
|
||||
title = 'Your website title'
|
||||
description = 'Your website description'
|
||||
sections = ["showcase", "about", "education", "experience", "client-and-work", "testimonial", "contact", "newsletter"]
|
||||
|
||||
# If you want to display an image logo in the header, you can add it here
|
||||
# logo = '/img/hugo.svg'
|
||||
homepageExperienceCount = 6
|
||||
|
||||
[params.analytics]
|
||||
## Analytics parameters.
|
||||
### Supported so far: Vercel (Page Insights, Analytics)
|
||||
### And Google (Tag Manager, Analytics)
|
||||
|
||||
# controls vercel page insights - disabled by default
|
||||
# to enable, just set to true
|
||||
vercelPageInsights = false
|
||||
vercelAnalytics = false
|
||||
|
||||
# google analytics and tag manager. to enable, set "enabled" to true
|
||||
# and add the tracking code (UA-something for analytics, GTM-something for tag manager)
|
||||
[params.analytics.googleAnalytics]
|
||||
code = "UA-XXXXX-Y"
|
||||
enabled = false
|
||||
[params.analytics.googleTagManager]
|
||||
code = "GTM-XXXXX"
|
||||
enabled = false
|
||||
|
||||
[build]
|
||||
[build.buildStats]
|
||||
disableClasses = false
|
||||
disableIDs = false
|
||||
disableTags = false
|
||||
enable = true
|
||||
|
||||
[params.languages.selector.disable]
|
||||
footer = false
|
||||
|
||||
[languages]
|
||||
[languages.en]
|
||||
disabled = false
|
||||
languageCode = 'en'
|
||||
languageDirection = 'ltr'
|
||||
languageName = 'English'
|
||||
title = ''
|
||||
weight = 0
|
||||
|
||||
[languages.en.menus]
|
||||
[[languages.en.menus.header]]
|
||||
name = 'About'
|
||||
URL = '#about'
|
||||
weight = 2
|
||||
[[languages.en.menus.header]]
|
||||
name = 'Portfolio'
|
||||
URL = '#portfolio'
|
||||
weight = 3
|
||||
# [[languages.en.menus.header]]
|
||||
# name = "Experience"
|
||||
# URL = "#experience"
|
||||
# weight = 4
|
||||
|
||||
[[languages.en.menus.header]]
|
||||
name = "Blog"
|
||||
URL = "/blog"
|
||||
weight = 5
|
||||
|
||||
[[languages.en.menus.header]]
|
||||
name = "Contact"
|
||||
URL = "#contact"
|
||||
weight = 6
|
||||
|
||||
[[languages.en.menus.footer]]
|
||||
name = "About"
|
||||
URL = "#about"
|
||||
weight = 2
|
||||
|
||||
[[languages.en.menus.footer]]
|
||||
name = "Portfolio"
|
||||
URL = "#portfolio"
|
||||
weight = 3
|
||||
|
||||
[[languages.en.menus.footer]]
|
||||
name = "Contact"
|
||||
URL = "#contact"
|
||||
weight = 4
|
||||
|
||||
|
||||
[languages.es]
|
||||
disabled = false
|
||||
languageCode = 'es'
|
||||
languageDirection = 'ltr'
|
||||
languageName = 'Español'
|
||||
title = ''
|
||||
weight = 0
|
||||
[[languages.es.menus.header]]
|
||||
name = 'Sobre mi'
|
||||
URL = '/es/#about'
|
||||
weight = 2
|
||||
[[languages.es.menus.header]]
|
||||
name = 'Portfolio'
|
||||
URL = '/es/#portfolio'
|
||||
weight = 3
|
||||
|
||||
# [[languages.es.menus.header]]
|
||||
# name = "Experiencia"
|
||||
# URL = "/es/#experience"
|
||||
# weight = 4
|
||||
|
||||
[[languages.es.menus.header]]
|
||||
name = "Blog"
|
||||
URL = "/es/blog"
|
||||
weight = 5
|
||||
|
||||
[[languages.es.menus.header]]
|
||||
name = "Contacto"
|
||||
URL = "/es/#contact"
|
||||
weight = 6
|
||||
|
||||
[[languages.es.menus.footer]]
|
||||
name = "Sobre mi"
|
||||
URL = "/es/#about"
|
||||
weight = 2
|
||||
|
||||
[[languages.es.menus.footer]]
|
||||
name = "Portfolio"
|
||||
URL = "/es/#portfolio"
|
||||
weight = 3
|
||||
|
||||
[[languages.es.menus.footer]]
|
||||
name = "Contact"
|
||||
URL = "/es/#contact"
|
||||
weight = 4
|
||||
|
||||
[languages.fr]
|
||||
disabled = false
|
||||
languageCode = 'fr'
|
||||
languageDirection = 'ltr'
|
||||
languageName = 'Français'
|
||||
title = ''
|
||||
weight = 0
|
||||
|
||||
[languages.fr.menus]
|
||||
[[languages.fr.menus.header]]
|
||||
name = 'About'
|
||||
URL = '#about'
|
||||
weight = 2
|
||||
[[languages.fr.menus.header]]
|
||||
name = 'Portfolio'
|
||||
URL = '#portfolio'
|
||||
weight = 3
|
||||
# [[languages.fr.menus.header]]
|
||||
# name = "Experience"
|
||||
# URL = "#experience"
|
||||
# weight = 4
|
||||
|
||||
[[languages.fr.menus.header]]
|
||||
name = "Blog"
|
||||
URL = "/blog"
|
||||
weight = 5
|
||||
|
||||
[[languages.fr.menus.header]]
|
||||
name = "Contact"
|
||||
URL = "#contact"
|
||||
weight = 6
|
||||
|
||||
[[languages.fr.menus.footer]]
|
||||
name = "About"
|
||||
URL = "#about"
|
||||
weight = 2
|
||||
|
||||
[[languages.fr.menus.footer]]
|
||||
name = "Portfolio"
|
||||
URL = "#portfolio"
|
||||
weight = 3
|
||||
|
||||
[[languages.fr.menus.footer]]
|
||||
name = "Contact"
|
||||
URL = "#contact"
|
||||
weight = 4
|
||||
|
||||
# Plugins
|
||||
[params.plugins]
|
||||
|
||||
# CSS Plugins
|
||||
[[params.plugins.css]]
|
||||
URL = "css/custom.css"
|
||||
[[params.plugins.css]]
|
||||
URL = "css/adritian-icons.css"
|
||||
## Optional, if you want print improvements (to PDF/printed)
|
||||
[[params.plugins.css]]
|
||||
URL = "css/bootstrap-print.css"
|
||||
|
||||
# JS Plugins
|
||||
[[params.plugins.js]]
|
||||
URL = "js/rad-animations.js"
|
||||
[[params.plugins.js]]
|
||||
URL = "js/sticky-header.js"
|
||||
[[params.plugins.js]]
|
||||
URL = "js/library/fontfaceobserver.js"
|
||||
|
||||
# SCSS Plugins
|
||||
[[params.plugins.scss]]
|
||||
URL = "scss/adritian.scss"
|
||||
|
||||
|
||||
# theme/color style
|
||||
[params.colorTheme]
|
||||
|
||||
## the following configuration would disable automatic theme selection
|
||||
# [params.colorTheme.auto]
|
||||
# disable = true
|
||||
# [params.colorTheme.forced]
|
||||
# theme = "dark"
|
||||
|
||||
## the following parameter will disable theme override in the footer
|
||||
# [params.colorTheme.selector.disable]
|
||||
# footer = true
|
||||
|
||||
|
||||
## by default we allow override AND automatic selection
|
||||
|
||||
[params.blog]
|
||||
layout = "default" # options: default, sidebar
|
||||
sidebarWidth = "25" # percentage width of the sidebar
|
||||
showCategories = true
|
||||
showRecentPosts = true
|
||||
recentPostCount = 5
|
||||
listStyle = "summary" # options: simple, summary
|
||||
```
|
||||
</details>
|
||||
|
||||
This configuration allows you to have a base to edit and adapt to your site, and see the available functionalities.
|
||||
Make sure to edit `baseURL`, `title` and `description`. You can edit the header links, as well as the languages to your needs.
|
||||
|
||||
1. Get the module: `hugo mod get -u`
|
||||
1. Execute `hugo mod npm pack` - this will generate a `package.json` file in the root folder of your site, with the dependencies for the theme.
|
||||
1. Execute `npm install` - this will install the dependencies for the theme (including bootstrap)
|
||||
1. (Optional, to override the defaults) Create a file `data/homepage.yml` with the contents of the [`exampleSite/data/homepage.yml`](https://github.com/zetxek/adritian-free-hugo-theme/blob/main/exampleSite/data/homepage.yml) file, and customize to your needs.
|
||||
1. Start Hugo with `hugo server`...
|
||||
1. 🎉 The theme is alive on http://localhost:1313/ (if everything went well)
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
### Editing the theme content
|
||||
|
||||
Currently the theme content is spread over multiple folders and files. We are working on simplifying this - but for now [this guide](https://adritian-demo.vercel.app/) should help you get started.
|
||||
|
||||
Some of the key files are:
|
||||
|
||||
|
||||
- `config.toml`: Main configuration for your Hugo site. Here you can set the site title, description, and theme specific settings such as:
|
||||
- menu structure (footer and header)
|
||||
- analytics (vercel, google)
|
||||
- blog settings (layout, sidebar contents, etc)
|
||||
- As well as some **required settings** for the theme to work properly (`module.mounts`, `params.plugins.css`)
|
||||
|
||||
- `data/homepage.yml`: homepage structure, content and sections - including social links, and the hero section.
|
||||
- `assets/`: Where you can store static assets such as images, CSS, and JavaScript files (you can add custom CSS and JS files with the `params.plugins.css` in `config.toml`).
|
||||
- `content/`: This is where your content files will live. The theme-specific ones are:
|
||||
- `content/blog/`: For blog posts.
|
||||
- `content/portfolio/`: For portfolio items.
|
||||
- `content/project/`: For projects.
|
||||
- `content/testimonial/`: For testimonials.
|
||||
- `content/education/`: For education items.
|
||||
- `content/experience/`: For experience items.
|
||||
- `content/client-and-work/`: For client and work items.
|
||||
|
||||
### Customizing the theme
|
||||
|
||||
Hugo allows you to customize the theme in many ways. You can override the theme's layouts, styles, and content.
|
||||
For that, you just need to locate the file you would like to change, copy it to your site's corresponding folder (`layouts`, `assets`, ...), and edit it.
|
||||
|
||||
**Note**: if you do this you will not benefit from theme updates, and that could lead to bugs. You can keep an eye on the [UPGRADING.md](https://github.com/zetxek/adritian-free-hugo-theme/blob/main/UPGRADING.md) file.
|
||||
|
||||
### Improving the theme
|
||||
|
||||
Maybe some of the customizations you do are worth a share. If you think that your changes could be useful for others, feel free to open a pull request in the [GitHub repository](https://github.com/zetxek/adritian-free-hugo-theme/pulls) - collaborations are very welcome, especially if the contributions come from real-world use cases.
|
||||
|
||||
### Publishing the site
|
||||
|
||||
Once you have your site ready, you can publish it to the web. There are many ways to do this, but the recommended one is to use a service like [Vercel](https://vercel.com/) or [Github Pages](https://pages.github.com/). Both services offer free hosting for static sites, and they can be connected to your GitHub repository for automatic deployments.
|
||||
|
||||
For that, you will need to have a CI/CD action that builds your site and deploys it to the service. You can find an example of a GitHub action in the demo site repository: [https://github.com/zetxek/adritian-demo/blob/main/.github/workflows/hugo.yml](https://github.com/zetxek/adritian-demo/blob/main/.github/workflows/hugo.yml)
|
||||
@@ -0,0 +1,64 @@
|
||||
+++
|
||||
title = 'Icons'
|
||||
date = 2025-02-28T08:05:05+01:00
|
||||
draft = false
|
||||
featured = true
|
||||
weight = 100
|
||||
[images]
|
||||
featured_image= '/img/blog/icons.png'
|
||||
+++
|
||||
|
||||
The theme has multiple icons available for use.
|
||||
|
||||
You can see them rendered here:
|
||||
|
||||
- {{< link icon="threads" url="https://example.com" >}} `threads`
|
||||
- {{< link icon="bluesky" url="https://example.com" >}} `bluesky`
|
||||
- {{< link icon="x-twitter" url="https://example.com" >}} `x-twitter`
|
||||
- {{< link icon="email" url="https://example.com" >}} `email`
|
||||
- {{< link icon="user" url="https://example.com" >}} `user`
|
||||
- {{< link icon="table-list" url="https://example.com" >}} `table-list`
|
||||
- {{< link icon="download" url="https://example.com" >}} `download`
|
||||
- {{< link icon="circle-info" url="https://example.com" >}} `circle-info`
|
||||
- {{< link icon="square-twitter" url="https://example.com" >}} `square-twitter`
|
||||
- {{< link icon="square-facebook" url="https://example.com" >}} `square-facebook`
|
||||
- {{< link icon="linkedin" url="https://example.com" >}} `linkedin`
|
||||
- {{< link icon="square-github" url="https://example.com" >}} `square-github`
|
||||
- {{< link icon="circle-arrow-left" url="https://example.com" >}} `circle-arrow-left`
|
||||
- {{< link icon="circle-arrow-right" url="https://example.com" >}} `circle-arrow-right`
|
||||
- {{< link icon="circle-arrow-up" url="https://example.com" >}} `circle-arrow-up`
|
||||
- {{< link icon="circle-arrow-down" url="https://example.com" >}} `circle-arrow-down`
|
||||
- {{< link icon="quote-left" url="https://example.com" >}} `quote-left`
|
||||
- {{< link icon="face-smile" url="https://example.com" >}} `face-smile`
|
||||
- {{< link icon="square-arrow-up-right" url="https://example.com" >}} `square-arrow-up-right`
|
||||
- {{< link icon="youtube" url="https://example.com" >}} `youtube`
|
||||
- {{< link icon="square-xing" url="https://example.com" >}} `square-xing`
|
||||
- {{< link icon="instagram" url="https://example.com" >}} `instagram`
|
||||
- {{< link icon="dribbble" url="https://example.com" >}} `dribbble`
|
||||
- {{< link icon="behance" url="https://example.com" >}} `behance`
|
||||
- {{< link icon="file-pdf" url="https://example.com" >}} `file-pdf`
|
||||
- {{< link icon="codepen" url="https://example.com" >}} `codepen`
|
||||
- {{< link icon="yelp" url="https://example.com" >}} `yelp`
|
||||
- {{< link icon="cloud-arrow-down" url="https://example.com" >}} `cloud-arrow-down`
|
||||
- {{< link icon="medium" url="https://example.com" >}} `medium`
|
||||
- {{< link icon="stack-overflow" url="https://example.com" >}} `stack-overflow`
|
||||
- {{< link icon="meetup" url="https://example.com" >}} `meetup`
|
||||
- {{< link icon="tiktok" url="https://example.com" >}} `tiktok`
|
||||
|
||||
You can use them in several ways.
|
||||
1. With the `link` shortcode, as shown above. For more information on how to use shortcodes like this one, visit our [shortcodes documentation page](/blog/shortcodes/).
|
||||
2. In the theme menus (header and footer):
|
||||
```
|
||||
[[languages.en.menus.header]]
|
||||
pre = "search"
|
||||
name = "Search"
|
||||
URL = "/search"
|
||||
weight = 7
|
||||
```
|
||||
3. Directly as HTML markup:
|
||||
```
|
||||
<i class="icon-email" aria-label="Contact"></i>
|
||||
```
|
||||
|
||||
Do you need more icons?
|
||||
You can check this github issue to check how to [add your own](https://github.com/zetxek/adritian-free-hugo-theme/pull/169), or if it's relevant for the community you can request one [via the issue tracker like @klangborste did](https://github.com/zetxek/adritian-free-hugo-theme/issues/168).
|
||||
@@ -0,0 +1,118 @@
|
||||
---
|
||||
title: 'New Theme Features Demo'
|
||||
date: 2025-02-20T10:00:00+00:00
|
||||
draft: false
|
||||
type: 'blog'
|
||||
toc: true
|
||||
tocSticky: true
|
||||
tags:
|
||||
- adritian
|
||||
- guide
|
||||
- advanced
|
||||
description: 'Discover the new features added to the Adritian theme including related posts, social sharing, table of contents, and comments support.'
|
||||
---
|
||||
|
||||
This post demonstrates the new features that have been added to the Adritian Hugo theme to make it more feature-complete and competitive with other popular Hugo themes.
|
||||
|
||||
## Overview of New Features
|
||||
|
||||
The Adritian theme now includes several enhancements that are commonly found in modern Hugo themes for personal websites and portfolios.
|
||||
|
||||
### Related Posts
|
||||
|
||||
At the end of each blog post, you'll now see a section showing related posts based on shared tags and publish dates. This helps visitors discover more relevant content on your site.
|
||||
|
||||
### Social Sharing Buttons
|
||||
|
||||
Share your content easily on social media platforms! The theme now includes built-in sharing buttons for:
|
||||
|
||||
- Twitter/X
|
||||
- LinkedIn
|
||||
- Facebook
|
||||
- Email
|
||||
|
||||
These buttons are automatically styled to match the theme's design and work seamlessly in both light and dark modes.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
This post has a table of contents enabled using the `toc: true` frontmatter parameter. The TOC helps readers navigate longer articles and can be made sticky on larger screens with `tocSticky: true`.
|
||||
|
||||
### How to Enable TOC
|
||||
|
||||
Simply add these parameters to your post's frontmatter:
|
||||
|
||||
```yaml
|
||||
---
|
||||
toc: true
|
||||
tocSticky: true # optional, makes TOC sticky on desktop
|
||||
---
|
||||
```
|
||||
|
||||
The table of contents automatically generates from your post headings and only appears when the post has more than 400 words.
|
||||
|
||||
## Enhanced Reading Experience
|
||||
|
||||
### Reading Time Display
|
||||
|
||||
Posts now show an estimated reading time alongside the word count, helping readers know how long an article will take to read.
|
||||
|
||||
### Last Modified Date
|
||||
|
||||
If you update your post's frontmatter with a `lastmod` date, it will be displayed in the post metadata, showing readers when the content was last updated.
|
||||
|
||||
## Comments Integration
|
||||
|
||||
The theme now supports popular comment systems:
|
||||
|
||||
- **Disqus** - Traditional comment system
|
||||
- **Giscus** - GitHub Discussions-powered comments
|
||||
- **Utterances** - GitHub Issues-powered comments
|
||||
|
||||
### Configuring Comments
|
||||
|
||||
Add this to your `hugo.toml` to enable comments:
|
||||
|
||||
```toml
|
||||
[params.comments]
|
||||
enabled = true
|
||||
provider = "giscus" # or "disqus" or "utterances"
|
||||
|
||||
[params.comments.giscus]
|
||||
repo = "username/repo"
|
||||
repoId = "your-repo-id"
|
||||
category = "General"
|
||||
categoryId = "your-category-id"
|
||||
```
|
||||
|
||||
## Social Sharing Configuration
|
||||
|
||||
Social sharing is enabled by default, but you can customize which platforms appear:
|
||||
|
||||
```toml
|
||||
[params.sharing]
|
||||
enabled = true
|
||||
twitter = true
|
||||
linkedin = true
|
||||
facebook = true
|
||||
email = true
|
||||
```
|
||||
|
||||
## Improved Taxonomy Support
|
||||
|
||||
The theme's tag and category displays have been enhanced with better styling and support for both light and dark modes.
|
||||
|
||||
## Multilingual Support
|
||||
|
||||
All new features include translations for:
|
||||
|
||||
- English (en)
|
||||
- Spanish (es)
|
||||
- French (fr)
|
||||
|
||||
You can easily add more languages by creating corresponding translation files in the `i18n/` directory.
|
||||
|
||||
## Conclusion
|
||||
|
||||
These new features make the Adritian theme more competitive with other popular Hugo themes while maintaining its focus on performance, accessibility, and clean design.
|
||||
|
||||
Try out the features by checking the related posts below, sharing this article, or exploring the table of contents!
|
||||
@@ -0,0 +1,136 @@
|
||||
+++
|
||||
title = 'How to Add Custom Icons to the Adritian Theme'
|
||||
date = 2025-02-28T08:05:05+01:00
|
||||
draft = false
|
||||
featured = true
|
||||
weight = 100
|
||||
[images]
|
||||
featured_image= '/img/blog/new-icon-3.png'
|
||||
+++
|
||||
|
||||
# How to Add Custom Icons to the Adritian Theme
|
||||
|
||||
{{< toc >}}
|
||||
|
||||
This guide will walk you through adding custom icons to your Adritian Hugo theme. Whether you need new social media icons, brand logos, or custom symbols, follow these steps to integrate them seamlessly.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before you begin, make sure you have:
|
||||
- Access to your Hugo site's source files
|
||||
- Basic understanding of file management
|
||||
- A web browser to access Fontello
|
||||
|
||||
## Overview
|
||||
|
||||
The Adritian theme uses a custom icon font generated with [Fontello](https://fontello.bableck.dev/). This is a fork of the original fontello, with additional font options.
|
||||
|
||||
To add new icons, you'll need to:
|
||||
|
||||
1. Export the current icon configuration
|
||||
2. Add new icons using Fontello
|
||||
3. Download and integrate the updated font files
|
||||
4. Update your site configuration
|
||||
|
||||
## Step 1: Export Current Configuration
|
||||
|
||||
1. Navigate to your theme's font directory: `static/fonts/`
|
||||
2. Locate the `config.json` file (see [the downstream version](https://github.com/zetxek/adritian-free-hugo-theme/blob/main/static/fonts/config.json))
|
||||
3. Keep this file ready - you'll upload it to Fontello
|
||||
|
||||
## Step 2: Import Configuration to Fontello
|
||||
|
||||
1. Go to [Fontello](https://fontello.bableck.dev/)
|
||||
2. Click the **import** button (wrench icon)
|
||||
3. Upload your `config.json` file
|
||||
4. Fontello will load your current icon set
|
||||
|
||||
## Step 3: Add New Icons
|
||||
|
||||

|
||||
|
||||
1. **Browse available icons** in Fontello's library
|
||||
2. **Click on icons** you want to add (they'll be highlighted)
|
||||
3. **Configure icon names** by clicking the pencil icon next to each selected icon
|
||||
4. **Set icon codes** if needed (Fontello usually assigns these automatically)
|
||||
|
||||
## Step 4: Download Updated Font
|
||||
|
||||
1. Click the **Download webfont** button in Fontello
|
||||
2. Save the generated ZIP file to your computer
|
||||
3. Extract the ZIP file contents
|
||||
|
||||
## Step 5 (option 1): Update Theme Files Manually
|
||||
|
||||
1. **Copy font files** from the downloaded ZIP to `static/fonts/`:
|
||||
- Copy `.woff`, `.woff2`, `.ttf`, and `.eot` files
|
||||
|
||||
2. **Update CSS file**:
|
||||
- Open the CSS file from the ZIP
|
||||
- **Important**: Replace all instances of `/font` with `/fonts`
|
||||
- Copy the updated CSS to your theme's CSS file
|
||||
|
||||
3. **Replace config.json**:
|
||||
- Copy the new `config.json` from the ZIP to `static/fonts/`
|
||||
|
||||
## Step 5 (option 2): Update Theme Files with npm script
|
||||
|
||||
You can use the theme helper (https://www.npmjs.com/package/@zetxek/adritian-theme-helper) to update the theme files.
|
||||
|
||||
You can read the theme helper font script here: https://www.npmjs.com/package/@zetxek/adritian-theme-helper#user-content-update-font-script
|
||||
|
||||
With the following command you can update the theme files with the new icons automatically:
|
||||
```bash
|
||||
ts-node scripts/update-font.ts <source> <destination>
|
||||
```
|
||||
|
||||
## Step 6: Using the New Icon
|
||||
|
||||
Reference your new icons in your content, as for the [pre-provided icons](/blog/icons/).
|
||||
|
||||
## Example Usage
|
||||
|
||||
```html
|
||||
<!-- In your templates -->
|
||||
<i class="icon-your-new-icon-name"></i>
|
||||
<!-- In markdown with HTML -->
|
||||
<i class="icon-your-new-icon-name"></i> Custom Icon Text
|
||||
<!-- In a markdown file -->
|
||||
{ {< link icon="icon-your-new-icon-name" url="https://example.com" >} }
|
||||
```
|
||||
Note: for the markdown file, no spaces between the curly braces - added so it gets displayed here properly.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Icons Not Displaying
|
||||
- **Check file paths**: Ensure fonts are in `static/fonts/`
|
||||
- **Verify CSS paths**: Make sure `/font` is changed to `/fonts`
|
||||
- **Clear browser cache**: Force refresh with Ctrl+F5 (or Cmd+Shift+R on Mac)
|
||||
|
||||
### Icons Show as Squares
|
||||
- **Font files missing**: Verify all font files were copied correctly
|
||||
- **CSS not loaded**: Check that your CSS includes the new icon definitions
|
||||
|
||||
### Build Errors
|
||||
- **Invalid CSS**: Check for syntax errors in your CSS file
|
||||
- **Missing files**: Ensure all required font files are present
|
||||
|
||||
## Best Practices
|
||||
|
||||
- **Keep backups**: Save your old `config.json` before making changes
|
||||
- **Test locally**: Always test icon changes on your local development site first
|
||||
- **Consistent naming**: Use clear, descriptive names for your custom icons
|
||||
- **Optimize selection**: Only include icons you actually need to keep font file sizes small
|
||||
- **Do not modify the theme, override the files in your own project**: Instead of modifying the theme, override the files in your own site project. That will ensure you can update the theme without losing your changes.
|
||||
- **If your icon choice is a popular one, consider contributing it to the theme**: If you think your icon choice is a popular one, consider contributing it to the theme. That will help other users and make the theme more complete.
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- [Fontello Documentation](https://github.com/fontello/fontello/wiki/Help)
|
||||
- [Adritian Theme Icons](/blog/icons/) - View all available icons
|
||||
|
||||
---
|
||||
|
||||
**Need help?** If you encounter issues following this guide, check the [theme's GitHub repository](https://github.com/zetxek/adritian-free-hugo-theme) for support and examples. If you still need help, you can open a new issue in the [theme's GitHub repository](https://github.com/zetxek/adritian-free-hugo-theme/issues).
|
||||
|
||||
|
||||
@@ -0,0 +1,380 @@
|
||||
---
|
||||
title: 'Other installation methods'
|
||||
date: 2025-02-11T14:38:33+02:00
|
||||
draft: false
|
||||
type: 'blog'
|
||||
tags:
|
||||
- adritian
|
||||
- guide
|
||||
---
|
||||
|
||||
This is a guide that extends the default instructions offered in [the theme readme](https://github.com/zetxek/adritian-free-hugo-theme/blob/main/README.md).
|
||||
|
||||
### Manual configuration
|
||||
If you prefer to manually set your site, you need to replace the contents of your config file (`hugo.toml`) file by these:
|
||||
|
||||
<details>
|
||||
<summary>hugo.toml configuration</summary>
|
||||
|
||||
```
|
||||
baseURL = "<your website url>"
|
||||
languageCode = "en"
|
||||
|
||||
[module]
|
||||
[module.hugoVersion]
|
||||
# We use hugo.Deps to list dependencies, which was added in Hugo 0.92.0
|
||||
min = "0.92.0"
|
||||
|
||||
[[module.imports]]
|
||||
path="github.com/zetxek/adritian-free-hugo-theme"
|
||||
|
||||
## Base mounts - so your site's assets are available
|
||||
[[module.mounts]]
|
||||
source = "archetypes"
|
||||
target = "archetypes"
|
||||
|
||||
[[module.mounts]]
|
||||
source = "assets"
|
||||
target = "assets"
|
||||
|
||||
[[module.mounts]]
|
||||
source = "i18n"
|
||||
target = "i18n"
|
||||
|
||||
[[module.mounts]]
|
||||
source = "layouts"
|
||||
target = "layouts"
|
||||
|
||||
[[module.mounts]]
|
||||
source = "static"
|
||||
target = "static"
|
||||
|
||||
# The following mounts are required for the theme to be able to load bootstrap
|
||||
# Remember also to copy the theme's `package.json` to your site, and run `npm install`
|
||||
[[module.mounts]]
|
||||
source = "node_modules/bootstrap/scss"
|
||||
target = "assets/scss/bootstrap"
|
||||
|
||||
[[module.mounts]]
|
||||
source = "node_modules/bootstrap/dist/js"
|
||||
target = "assets/js/bootstrap"
|
||||
|
||||
[[module.mounts]]
|
||||
source = "node_modules/bootstrap/dist/js/bootstrap.bundle.min.js"
|
||||
target = "assets/js/vendor/bootstrap.bundle.min.js"
|
||||
|
||||
## Optional, if you want print improvements (to PDF/printed)
|
||||
[[module.mounts]]
|
||||
source = "node_modules/bootstrap-print-css/css/bootstrap-print.css"
|
||||
target = "assets/css/bootstrap-print.css"
|
||||
|
||||
[params]
|
||||
|
||||
title = 'Your website title'
|
||||
description = 'Your website description'
|
||||
sections = ["showcase", "about", "education", "experience", "client-and-work", "testimonial", "contact", "newsletter"]
|
||||
|
||||
# If you want to display an image logo in the header, you can add it here
|
||||
# logo = '/img/hugo.svg'
|
||||
homepageExperienceCount = 6
|
||||
|
||||
[params.analytics]
|
||||
## Analytics parameters.
|
||||
### Supported so far: Vercel (Page Insights, Analytics)
|
||||
### And Google (Tag Manager, Analytics)
|
||||
|
||||
# controls vercel page insights - disabled by default
|
||||
# to enable, just set to true
|
||||
vercelPageInsights = false
|
||||
vercelAnalytics = false
|
||||
|
||||
# google analytics and tag manager. to enable, set "enabled" to true
|
||||
# and add the tracking code (UA-something for analytics, GTM-something for tag manager)
|
||||
[params.analytics.googleAnalytics]
|
||||
code = "UA-XXXXX-Y"
|
||||
enabled = false
|
||||
[params.analytics.googleTagManager]
|
||||
code = "GTM-XXXXX"
|
||||
enabled = false
|
||||
|
||||
[build]
|
||||
[build.buildStats]
|
||||
disableClasses = false
|
||||
disableIDs = false
|
||||
disableTags = false
|
||||
enable = true
|
||||
|
||||
[params.languages.selector.disable]
|
||||
footer = false
|
||||
|
||||
[languages]
|
||||
[languages.en]
|
||||
disabled = false
|
||||
languageCode = 'en'
|
||||
languageDirection = 'ltr'
|
||||
languageName = 'English'
|
||||
title = ''
|
||||
weight = 0
|
||||
|
||||
[languages.en.menus]
|
||||
[[languages.en.menus.header]]
|
||||
name = 'About'
|
||||
URL = '#about'
|
||||
weight = 2
|
||||
[[languages.en.menus.header]]
|
||||
name = 'Portfolio'
|
||||
URL = '#portfolio'
|
||||
weight = 3
|
||||
# [[languages.en.menus.header]]
|
||||
# name = "Experience"
|
||||
# URL = "#experience"
|
||||
# weight = 4
|
||||
|
||||
[[languages.en.menus.header]]
|
||||
name = "Blog"
|
||||
URL = "/blog"
|
||||
weight = 5
|
||||
|
||||
[[languages.en.menus.header]]
|
||||
name = "Contact"
|
||||
URL = "#contact"
|
||||
weight = 6
|
||||
|
||||
[[languages.en.menus.footer]]
|
||||
name = "About"
|
||||
URL = "#about"
|
||||
weight = 2
|
||||
|
||||
[[languages.en.menus.footer]]
|
||||
name = "Portfolio"
|
||||
URL = "#portfolio"
|
||||
weight = 3
|
||||
|
||||
[[languages.en.menus.footer]]
|
||||
name = "Contact"
|
||||
URL = "#contact"
|
||||
weight = 4
|
||||
|
||||
|
||||
[languages.es]
|
||||
disabled = false
|
||||
languageCode = 'es'
|
||||
languageDirection = 'ltr'
|
||||
languageName = 'Español'
|
||||
title = ''
|
||||
weight = 0
|
||||
[[languages.es.menus.header]]
|
||||
name = 'Sobre mi'
|
||||
URL = '/es/#about'
|
||||
weight = 2
|
||||
[[languages.es.menus.header]]
|
||||
name = 'Portfolio'
|
||||
URL = '/es/#portfolio'
|
||||
weight = 3
|
||||
|
||||
# [[languages.es.menus.header]]
|
||||
# name = "Experiencia"
|
||||
# URL = "/es/#experience"
|
||||
# weight = 4
|
||||
|
||||
[[languages.es.menus.header]]
|
||||
name = "Blog"
|
||||
URL = "/es/blog"
|
||||
weight = 5
|
||||
|
||||
[[languages.es.menus.header]]
|
||||
name = "Contacto"
|
||||
URL = "/es/#contact"
|
||||
weight = 6
|
||||
|
||||
[[languages.es.menus.footer]]
|
||||
name = "Sobre mi"
|
||||
URL = "/es/#about"
|
||||
weight = 2
|
||||
|
||||
[[languages.es.menus.footer]]
|
||||
name = "Portfolio"
|
||||
URL = "/es/#portfolio"
|
||||
weight = 3
|
||||
|
||||
[[languages.es.menus.footer]]
|
||||
name = "Contact"
|
||||
URL = "/es/#contact"
|
||||
weight = 4
|
||||
|
||||
[languages.fr]
|
||||
disabled = false
|
||||
languageCode = 'fr'
|
||||
languageDirection = 'ltr'
|
||||
languageName = 'Français'
|
||||
title = ''
|
||||
weight = 0
|
||||
|
||||
[languages.fr.menus]
|
||||
[[languages.fr.menus.header]]
|
||||
name = 'About'
|
||||
URL = '#about'
|
||||
weight = 2
|
||||
[[languages.fr.menus.header]]
|
||||
name = 'Portfolio'
|
||||
URL = '#portfolio'
|
||||
weight = 3
|
||||
# [[languages.fr.menus.header]]
|
||||
# name = "Experience"
|
||||
# URL = "#experience"
|
||||
# weight = 4
|
||||
|
||||
[[languages.fr.menus.header]]
|
||||
name = "Blog"
|
||||
URL = "/blog"
|
||||
weight = 5
|
||||
|
||||
[[languages.fr.menus.header]]
|
||||
name = "Contact"
|
||||
URL = "#contact"
|
||||
weight = 6
|
||||
|
||||
[[languages.fr.menus.footer]]
|
||||
name = "About"
|
||||
URL = "#about"
|
||||
weight = 2
|
||||
|
||||
[[languages.fr.menus.footer]]
|
||||
name = "Portfolio"
|
||||
URL = "#portfolio"
|
||||
weight = 3
|
||||
|
||||
[[languages.fr.menus.footer]]
|
||||
name = "Contact"
|
||||
URL = "#contact"
|
||||
weight = 4
|
||||
|
||||
# Plugins
|
||||
[params.plugins]
|
||||
|
||||
# CSS Plugins
|
||||
[[params.plugins.css]]
|
||||
URL = "css/custom.css"
|
||||
[[params.plugins.css]]
|
||||
URL = "css/adritian-icons.css"
|
||||
## Optional, if you want print improvements (to PDF/printed)
|
||||
[[params.plugins.css]]
|
||||
URL = "css/bootstrap-print.css"
|
||||
|
||||
# JS Plugins
|
||||
[[params.plugins.js]]
|
||||
URL = "js/rad-animations.js"
|
||||
[[params.plugins.js]]
|
||||
URL = "js/sticky-header.js"
|
||||
[[params.plugins.js]]
|
||||
URL = "js/library/fontfaceobserver.js"
|
||||
|
||||
# SCSS Plugins
|
||||
[[params.plugins.scss]]
|
||||
URL = "scss/adritian.scss"
|
||||
|
||||
|
||||
# theme/color style
|
||||
[params.colorTheme]
|
||||
|
||||
## the following configuration would disable automatic theme selection
|
||||
# [params.colorTheme.auto]
|
||||
# disable = true
|
||||
# [params.colorTheme.forced]
|
||||
# theme = "dark"
|
||||
|
||||
## the following parameter will disable theme override in the footer
|
||||
# [params.colorTheme.selector.disable]
|
||||
# footer = true
|
||||
|
||||
|
||||
## by default we allow override AND automatic selection
|
||||
|
||||
[params.blog]
|
||||
layout = "default" # options: default, sidebar
|
||||
sidebarWidth = "25" # percentage width of the sidebar
|
||||
showCategories = true
|
||||
showRecentPosts = true
|
||||
recentPostCount = 5
|
||||
listStyle = "summary" # options: simple, summary
|
||||
```
|
||||
|
||||
1. Get the module: `hugo mod get -u`
|
||||
1. Execute `hugo mod npm pack` - this will generate a `package.json` file in the root folder of your site, with the dependencies for the theme.
|
||||
1. Execute `npm install` - this will install the dependencies for the theme (including bootstrap)
|
||||
1. (Optional, to override the defaults) Create a file `data/homepage.yml` with the contents of the [`exampleSite/data/homepage.yml`](https://github.com/zetxek/adritian-free-hugo-theme/blob/main/exampleSite/data/homepage.yml) file, and customize to your needs (__note: this file is not included in your theme if you use hugo modules, download it manually from the repository__)
|
||||
|
||||
|
||||
</details>
|
||||
|
||||
### Traditional Installation (as git submodule)
|
||||
|
||||
If you prefer not to use Hugo Modules, you can still install the theme as a git submodule.
|
||||
The guide is very similar to [official "Quick Start"](https://gohugo.io/getting-started/quick-start/#create-a-site), just changing the theme URL in the `git submodule add` command:
|
||||
|
||||
<details>
|
||||
<summary>Old instructions for git submodules</summary>
|
||||
|
||||
- Create a new Hugo site (this will create a new folder): `hugo new site <your website's name>`
|
||||
- Enter the newly created folder: `cd <your website's name>/`
|
||||
- Initialize hugo modules: `hugo mod init github.com/<your_user>/<your_project>`
|
||||
- Install PostCSS: execute `npm i -D postcss postcss-cli autoprefixer` from the top-level site folder [check [Hugo's official docs](https://gohugo.io/hugo-pipes/postcss/)].
|
||||
- Add adritian-free-hugo-theme as a module dependency, by adding
|
||||
```
|
||||
[module]
|
||||
[[module.imports]]
|
||||
path = 'github.com/zetxek/adritian-free-hugo-theme'
|
||||
```
|
||||
To your `hugo.toml` file, and executing `hugo mod get -u`
|
||||
|
||||
- Replace the `hugo.toml` file in the project's root directory with the contents of [themes/adritian-free-hugo-theme/exampleSite/config.toml](https://github.com/zetxek/adritian-free-hugo-theme/blob/main/exampleSite/hugo.toml). If you are using the git submodules, you can execute `cp themes/adritian-free-hugo-theme/exampleSite/hugo.toml hugo.toml` (*executed from the website root folder*), otherwise just copy and paste the contents.
|
||||
- Create the file `data/homepage.yml`, with the initial contents of the [`exampleSite/data/homepage.yml`](https://github.com/zetxek/adritian-free-hugo-theme/blob/main/exampleSite/data/homepage.yml). This will serve as your starting point to customize your home content ✍️
|
||||
- Start Hugo with `hugo server -D`
|
||||
- 🎉 The theme is alive on http://localhost:1313/
|
||||
|
||||
|
||||
_Optional, and only for git submodule or when you download the whole repo:_
|
||||
if you want to preview the theme with the example content before deciding if it's what you are looking for, you can run the theme with the example content:
|
||||
```
|
||||
cd themes/adritian-free-hugo-theme/exampleSite
|
||||
hugo server --themesDir ../..
|
||||
```
|
||||
|
||||
Note: The `exampleSite` is not downloaded when installing the Hugo module. That's why this works only as git submodule or full repo download.
|
||||
</details>
|
||||
|
||||
|
||||
---
|
||||
|
||||
After this, you can start adding your own content to the site, and customize the configuration.
|
||||
|
||||
If everything has gone well asn you have the right configuration, the output for the `serve` command will be something like this:
|
||||
<details>
|
||||
<summary>Example output for the serve command</summary>
|
||||
|
||||
```
|
||||
adritian-demo git:(master) ✗ hugo server -D
|
||||
Watching for changes in /Users/adrianmorenopena/tmp/theme-test/themes/adritian-free-hugo-theme/{archetypes,assets,data,exampleSite,i18n,layouts,static}
|
||||
Watching for config changes in /Users/adrianmorenopena/tmp/theme-test/themes/adritian-free-hugo-theme/exampleSite/hugo.toml
|
||||
Start building sites …
|
||||
hugo v0.136.2+extended darwin/arm64 BuildDate=2024-10-17T14:30:05Z VendorInfo=brew
|
||||
|
||||
|
||||
| EN | ES | FR
|
||||
-------------------+----+----+-----
|
||||
Pages | 24 | 10 | 8
|
||||
Paginator pages | 0 | 0 | 0
|
||||
Non-page files | 0 | 0 | 0
|
||||
Static files | 90 | 90 | 90
|
||||
Processed images | 24 | 0 | 0
|
||||
Aliases | 1 | 0 | 0
|
||||
Cleaned | 0 | 0 | 0
|
||||
|
||||
Built in 1788 ms
|
||||
Environment: "development"
|
||||
Serving pages from disk
|
||||
Running in Fast Render Mode. For full rebuilds on change: hugo server --disableFastRender
|
||||
Web Server is available at http://localhost:1313/ (bind address 127.0.0.1)
|
||||
Press Ctrl+C to stop
|
||||
```
|
||||
</details>
|
||||
@@ -0,0 +1,19 @@
|
||||
+++
|
||||
title = 'Sample content: featured image'
|
||||
date = 2024-06-24T14:38:33+02:00
|
||||
draft = false
|
||||
type = 'blog'
|
||||
|
||||
[images]
|
||||
featured_image = '/img/blog/feature-sample-3.png'
|
||||
+++
|
||||
|
||||
Sample blog content. Like a lorem ipsum but saying something more interesting.
|
||||
|
||||

|
||||
|
||||
Welcome to the world of "Content Ipsum," the fresh alternative to the classic lorem ipsum. It's the perfect blend for designers and writers who crave a dash of creativity and meaning in their placeholder text. Imagine a text that not only fills the space but also sparks the imagination, a text that weaves tales of innovation, inspiration, and the endless possibilities that creativity brings.
|
||||
|
||||
In the realm of "Content Ipsum," every paragraph is a journey through the wonders of the human mind, a celebration of the achievements that have shaped our world, and a look into the future that awaits us. From the depths of the ocean to the farthest reaches of the universe, "Content Ipsum" takes you on an adventure that captivates and informs.
|
||||
|
||||
So, the next time you're crafting a design or drafting a document, let "Content Ipsum" infuse your work with the spirit of discovery and the joy of creation. It's more than just text; it's a narrative that connects, engages, and inspires. Welcome to the evolution of placeholder text – where every word is a story waiting to be told.
|
||||
@@ -0,0 +1,162 @@
|
||||
---
|
||||
title: 'Sample content: formatting styles'
|
||||
date: 2024-06-21T14:38:33+02:00
|
||||
draft: false
|
||||
type: 'blog'
|
||||
tags:
|
||||
- sample
|
||||
- lorem-ipsum
|
||||
---
|
||||
|
||||
Sample blog content. Like a lorem ipsum but saying something more interesting.
|
||||
|
||||
We will show some content that is supported:
|
||||
|
||||
## Basic Syntax
|
||||
|
||||
### Headings
|
||||
|
||||
```
|
||||
# Heading 1
|
||||
## Heading 2
|
||||
### Heading 3
|
||||
#### Heading 4
|
||||
##### Heading 5
|
||||
###### Heading 6
|
||||
```
|
||||
|
||||
## Heading 2
|
||||
### Heading 3
|
||||
#### Heading 4
|
||||
##### Heading 5
|
||||
###### Heading 6
|
||||
|
||||
### Emphasis
|
||||
|
||||
```text
|
||||
*This text will be italic*
|
||||
_This will also be italic_
|
||||
|
||||
**This text will be bold**
|
||||
__This will also be bold__
|
||||
|
||||
_You **can** combine them_
|
||||
```
|
||||
|
||||
*This text will be italic*
|
||||
|
||||
_This will also be italic_
|
||||
|
||||
**This text will be bold**
|
||||
|
||||
__This will also be bold__
|
||||
|
||||
_You **can** combine them_
|
||||
|
||||
### Lists
|
||||
|
||||
#### Unordered
|
||||
|
||||
```
|
||||
* Item 1
|
||||
* Item 2
|
||||
* Item 2a
|
||||
* Item 2b
|
||||
```
|
||||
|
||||
* Item 1
|
||||
* Item 2
|
||||
* Item 2a
|
||||
* Item 2b
|
||||
|
||||
#### Ordered
|
||||
|
||||
```
|
||||
1. Item 1
|
||||
2. Item 2
|
||||
3. Item 3
|
||||
1. Item 3a
|
||||
2. Item 3b
|
||||
```
|
||||
|
||||
#### Nested
|
||||
|
||||
- This is an unordered list sample 1
|
||||
- This is an unordered list sample 2
|
||||
- This is an unordered indented list sample 1
|
||||
- This is an unordered indented list sample 2
|
||||
1. This is an ordered list sample 1
|
||||
2. This is an ordered list sample 2
|
||||
1. This is an ordered indented list sample 1
|
||||
2. This is an ordered indented list sample 2
|
||||
|
||||
### Images
|
||||
|
||||
```markdown
|
||||

|
||||
```
|
||||
|
||||

|
||||
|
||||
### Links
|
||||
|
||||
```markdown
|
||||
[Hugo](https://gohugo.io)
|
||||
```
|
||||
|
||||
[Hugo](https://gohugo.io)
|
||||
|
||||
### Blockquotes
|
||||
|
||||
```markdown
|
||||
As Newton said:
|
||||
|
||||
> If I have seen further it is by standing on the shoulders of Giants.
|
||||
```
|
||||
|
||||
> If I have seen further it is by standing on the shoulders of Giants.
|
||||
|
||||
### Inline Code
|
||||
|
||||
```markdown
|
||||
Inline `code` has `back-ticks around` it.
|
||||
```
|
||||
|
||||
Inline `code` has `back-ticks around` it.
|
||||
|
||||
### Code Blocks
|
||||
|
||||
#### Syntax Highlighting
|
||||
|
||||
````markdown
|
||||
```go
|
||||
func main() {
|
||||
fmt.Println("Hello World")
|
||||
}
|
||||
```
|
||||
````
|
||||
|
||||
```go
|
||||
func main() {
|
||||
fmt.Println("Hello World")
|
||||
}
|
||||
```
|
||||
|
||||
### Tables
|
||||
|
||||
```markdown
|
||||
| Syntax | Description |
|
||||
| --------- | ----------- |
|
||||
| Header | Title |
|
||||
| Paragraph | Text |
|
||||
```
|
||||
|
||||
| Syntax | Description |
|
||||
| --------- | ----------- |
|
||||
| Header | Title |
|
||||
| Paragraph | Text |
|
||||
|
||||
## References
|
||||
|
||||
- [Markdown Syntax](https://www.markdownguide.org/basic-syntax/)
|
||||
- [Hugo Markdown](https://gohugo.io/content-management/formats/#markdown)
|
||||
@@ -0,0 +1,216 @@
|
||||
---
|
||||
title: 'Theme shortcodes'
|
||||
date: 2025-02-14T14:38:33+02:00
|
||||
draft: false
|
||||
type: 'blog'
|
||||
tags:
|
||||
- adritian
|
||||
- guide
|
||||
---
|
||||
|
||||
Shortcodes are a convenient [functionality of Hugo](https://gohugo.io/content-management/shortcodes/), allowing you to embed templates or layouts within other content.
|
||||
|
||||
Some of the example built-in shortcodes are Instagram posts, YouTube videos, QR codes, etc. You can find [the full list in the official docs](https://gohugo.io/content-management/shortcodes/#embedded).
|
||||
|
||||
The theme provides custom shortcodes to allow you to customize your landing page as you want:
|
||||
|
||||
- `toc`: Generates a styled table of contents from page headings.
|
||||
- `education-list`: Displays a list of educational qualifications.
|
||||
- `experience-list`: Shows a list of professional experiences.
|
||||
- `platform-links`: Embeds links to various platforms with icons.
|
||||
- `newsletter-section`: Adds a section for newsletter subscription.
|
||||
- `link`: Creates a hyperlink with an icon.
|
||||
- `experience-section`: Inserts a detailed experience section.
|
||||
- `contact-section`: Adds a contact information section, with a customizable form and information.
|
||||
- `client-and-work-section`: Displays a section for clients and work.
|
||||
- `about-section`: About section, with image, buttons for call to action and image.
|
||||
- `testimonial-section`: Adds references from customers, colleagues, etc.
|
||||
- `showcase`: two-column block with a full-width image to the left, and a text snippet to the right. Great for a call to action or introduction of the person (assuming it's a personal website).
|
||||
- `text-section`: utility shortcode used to render text in some parts of the theme where it would otherwise be full-width, appearing "too floaty". See [the github issue #260 for context](https://github.com/zetxek/adritian-free-hugo-theme/issues/260).
|
||||
- `spacer`: Adds vertical spacing before the next element.
|
||||
|
||||
The shortcodes can be customized with different arguments:
|
||||
|
||||
- `toc`: Automatically generates a table of contents from the page's headings (H2, H3, etc.)
|
||||
- `heading`: Custom heading text for the table of contents. Defaults to "Table of Contents".
|
||||
- `showHeading`: Boolean value to show or hide the heading. Defaults to true.
|
||||
- `class`: Custom CSS class for styling. Defaults to "table-of-contents".
|
||||
- **Features**:
|
||||
- Only displays if the page contains headings
|
||||
- Supports nested heading levels with proper indentation
|
||||
- Responsive design with dark mode support
|
||||
- Smooth scrolling to sections
|
||||
- Clean, modern styling with hover effects
|
||||
- **Usage Examples**:
|
||||
- `{{</* toc */>}}`: Basic table of contents with default settings
|
||||
- `{{</* toc heading="Contents" */>}}`: Custom heading
|
||||
- `{{</* toc showHeading=false */>}}`: Hide the heading
|
||||
- `{{</* toc class="sidebar-toc" */>}}`: Custom CSS class
|
||||
- `{{</* toc heading="Article Contents" class="custom-toc" */>}}`: Multiple parameters
|
||||
|
||||
- `education-list`:
|
||||
- `title`: The title of the education section.
|
||||
- `items`: A list of educational qualifications, they're provided by the `education` content type pages. From each, the `year`, `university`, and `degree` will be used.
|
||||
- `sectionId`: Optional. Overrides the default HTML id for the section. If not provided, the default id is used.
|
||||
|
||||
- `experience-list`:
|
||||
- `title`: The title of the experience section (optional). When provided, an h2 heading is added above the list.
|
||||
- `padding`: Controls whether the section has padding. Set to "true" by default.
|
||||
- `items`: A list of professional experiences, coming from the `experience` content type. For each of them, the `companyLogo`, `duration`, `jobTitle`, `location`, and `Content` are used.
|
||||
- **Note**: On mobile devices, a separator line is automatically added between experience entries for better readability.
|
||||
- `sectionId`: Optional. Overrides the default HTML id for the section. If not provided, the default id is used.
|
||||
|
||||
- `platform-links`:
|
||||
- `platforms`: A container to place social links inside. Usually you'll want to use it with a list of `link` nested (see below)
|
||||
- `sectionId`: Optional. Overrides the default HTML id for the section. If not provided, the default id is used.
|
||||
|
||||
- `link`: links to social platforms (facebook, linkedin, etc)
|
||||
- `url`: The URL for the hyperlink.
|
||||
- `icon`: The icon to display with the link.
|
||||
- `sectionId`: Optional. Overrides the default HTML id for the section. If not provided, the default id is used.
|
||||
|
||||
- `newsletter-section`:
|
||||
- **Form Configuration**:
|
||||
- `form_action`: The URL where the newsletter form data will be submitted. Falls back to site data configuration (typically set in data/homepage.yaml).
|
||||
- `form_method`: The HTTP method for form submission (typically "POST" or "GET"). Falls back to site data configuration.
|
||||
- **Section Content**:
|
||||
- `newsletter_title`: Sets the main heading/title for the newsletter section. Falls back to i18n value "newsletter_title".
|
||||
- `newsletter_placeholder`: Placeholder text displayed in the email input field. Falls back to i18n value "newsletter_placeholder".
|
||||
- `newsletter_button`: Text displayed on the subscription button. Falls back to i18n value "newsletter_button".
|
||||
- **Response Messages**:
|
||||
- `newsletter_success_message`: Message displayed to users after successful subscription. Falls back to i18n value "newsletter_success_message".
|
||||
- `newsletter_error_message`: Message displayed to users if subscription fails. Falls back to i18n value "newsletter_error_message".
|
||||
- `newsletter_note`: Small text/disclaimer shown below the form (typically mentions privacy policy). Falls back to i18n value "newsletter_note".
|
||||
- `sectionId`: Optional. Overrides the default HTML id for the section. If not provided, the default id is used.
|
||||
|
||||
- `experience-section`:
|
||||
- `title`: The title of the experience section.
|
||||
- `experiences`: A detailed list of experiences, coming from the `experience` content type pages.
|
||||
- `sectionId`: Optional. Overrides the default HTML id for the section. If not provided, the default id is used.
|
||||
|
||||
- `contact-section`:
|
||||
- `title`: Sets the main heading/title for the contact section. Falls back to the i18n value "contact_title" if not provided.
|
||||
- **Form Configuration**:
|
||||
- `form_action`: The URL where the form data will be submitted. Commonly used with services like Formspree. Falls back to site data configuration.
|
||||
- `form_method`: The HTTP method for form submission (typically "POST" or "GET"). Falls back to site data configuration.
|
||||
- **Form Field Placeholders**:
|
||||
- `contact_form_name`: Placeholder text for the name input field. Falls back to i18n value.
|
||||
- `contact_form_email`: Placeholder text for the email input field. Falls back to i18n value.
|
||||
- `contact_form_phone`: Placeholder text for the phone input field. Falls back to i18n value.
|
||||
- `contact_form_message`: Placeholder text for the message textarea. Falls back to i18n value.
|
||||
- `contact_form_rows`: Number of rows for the message textarea. Defaults to 2 if not specified.
|
||||
- `contact_button`: Text for the form submission button. Falls back to i18n value.
|
||||
- **Contact Information**:
|
||||
- `contact_phone_title`: Heading for the phone contact section. Falls back to i18n value.
|
||||
- `contact_phone_number`: Phone number to display. Falls back to i18n value.
|
||||
- `contact_email_title`: Heading for the email contact section. Falls back to i18n value.
|
||||
- `contact_email_email`: Email address to display. Falls back to i18n value.
|
||||
- `contact_address_title`: Heading for the address section. Falls back to i18n value.
|
||||
- `contact_address_address`: Physical address or location information. Falls back to i18n value.
|
||||
- `sectionId`: Optional. Overrides the default HTML id for the section. If not provided, the default id is used.
|
||||
|
||||
- `client-and-work-section`: this shortcode doesn't use as many arguments - as much of the content comes from other content pages.
|
||||
- `title`: The title of the client and work section.
|
||||
- `clients`: A list of clients, coming from the `client` content type (each with `title`, `link`, `logo`)
|
||||
- `projects`: A list of projects, coming from the `project` content type (each with `title`, `content`, `button.URL`, `button.btnText`, `button.icon`, `image`).
|
||||
- `sectionId`: Optional. Overrides the default HTML id for the section. If not provided, the default id is used.
|
||||
|
||||
- `about-section`:
|
||||
- Content Arguments
|
||||
- `intro_title` - Sets the main heading/title for the about section. Falls back to the title from site data if not provided.
|
||||
|
||||
- `intro_description` - Contains the HTML content or description text for the about section. Falls back to the content from site data if not provided.
|
||||
|
||||
- `imgSrc` - Specifies the path to the image displayed in the about section. Falls back to the image defined in site data if not provided.
|
||||
|
||||
- `imgWidth` - Specifies the width for the image.
|
||||
|
||||
- `imgHeight` - Specifies the height for the image.
|
||||
|
||||
- `imgScale` - Specifies the scale used for the image (for example, `0.5` if the high resolution image is double the size of the smaller one) This is only considered if neither imgWidth nor imgHeight is used.
|
||||
|
||||
- `v_align` - Controls the vertical alignment of the text content relative to the image. Accepts "center" (default), "top", or "bottom".
|
||||
- `text_align` - **Deprecated**: Use `v_align` instead. Maintains backwards compatibility.
|
||||
- `h_align` - Controls the horizontal alignment of the text content. Accepts "left" (default), "center", or "right".
|
||||
|
||||
**Alignment Examples**:
|
||||
- `v_align="center" h_align="left"` - Text vertically centered, left-aligned (default)
|
||||
- `v_align="top" h_align="center"` - Text at top, horizontally centered
|
||||
- `v_align="bottom" h_align="right"` - Text at bottom, right-aligned
|
||||
|
||||
- Primary Button Arguments
|
||||
- `button1_enable` - Boolean value to show or hide the primary button. Defaults to the value from site data.
|
||||
|
||||
- `button1_icon` - icon class to display before the button text. Falls back to the icon from site data.
|
||||
|
||||
- `button1_url` - Target URL for the primary button. Falls back to the URL from site data.
|
||||
|
||||
- `button1_text` - Text label to display on the primary button. Falls back to the text from site data.
|
||||
|
||||
- Secondary Button Arguments
|
||||
- `button2_enable` - Boolean value to show or hide the secondary button. Defaults to the value from site data.
|
||||
|
||||
- `button2_icon` - FontAwesome or icon class to display before the button text. Falls back to the icon from site data.
|
||||
|
||||
- `button2_url` - Target URL for the secondary button. Falls back to the URL from site data.
|
||||
|
||||
- `button2_text` - Text label to display on the secondary button. Falls back to the text from site data.
|
||||
|
||||
- `sectionId`: Optional. Overrides the default HTML id for the section. If not provided, the default id is used.
|
||||
|
||||
- `testimonial-section`:
|
||||
- `title`: The title of the testimonial section.
|
||||
- `testimonials`: A list of testimonials, provided by the `testimonial` content type pages. The partial dynamically pulls content from pages with type "testimonial". Each testimonial page should include:
|
||||
- `Content`: The main testimonial text (the actual quote)
|
||||
- Meta Information:
|
||||
- `name`: The name of the person giving the testimonial
|
||||
- `position`: The job title or role of the person giving the testimonial
|
||||
- `image.src`: Path to the image of the testimonial author
|
||||
- `sectionId`: Optional. Overrides the default HTML id for the section. If not provided, the default id is used.
|
||||
|
||||
- `showcase`:
|
||||
- **Content Arguments**:
|
||||
- `title`: Sets the main heading/title for the showcase section. Falls back to the i18n value "showcase_title".
|
||||
- `subtitle`: Sets the subtitle text that appears below the main heading. Falls back to the i18n value "showcase_subtitle".
|
||||
- `description`: Contains the HTML content or description text for the showcase section. Falls back to the i18n value "showcase_description".
|
||||
- **Button Configuration**:
|
||||
- `button_text`: Text label to display on the button. If not provided or set to false, the button will not be displayed.
|
||||
- `button_icon`: Icon class to display before the button text. Falls back to the icon from site data.
|
||||
- `button_url`: Target URL for the button. Falls back to the URL from site data.
|
||||
- **Image Configuration**:
|
||||
- `imgSrc`: Specifies the path to the image displayed in the showcase section. Falls back to the image defined in site data.
|
||||
- `imgWidth` - Specifies the width for the image.
|
||||
- `imgHeight` - Specifies the height for the image.
|
||||
- `imgScale` - Specifies the scale used for the image (for example, `0.5` if the high resolution image is double the size of the smaller one) This is only considered if neither imgWidth nor imgHeight is used.
|
||||
- **Social Media**:
|
||||
- `social_links`: Array of social media platform links to display at the bottom of the showcase. Each item should have a URL and icon property.
|
||||
- **Responsive Behavior**:
|
||||
- The showcase uses a two-column layout on desktop devices (≥768px) and stacks columns vertically on mobile devices, with the image appearing above the text content.
|
||||
- **Inner Content**:
|
||||
- The shortcode can also accept inner content that will be rendered in a separate div with class "inner-content".
|
||||
- `sectionId`: Optional. Overrides the default HTML id for the section. If not provided, the default id is used.
|
||||
|
||||
- `text-section`:
|
||||
- **Content Arguments**:
|
||||
- `title`: Sets the main heading/title for the text section (optional).
|
||||
- `subtitle`: Sets the subtitle text that appears below the main heading (optional).
|
||||
- **Layout Options**:
|
||||
- `padding`: Controls whether the section has padding. Set to "true" by default.
|
||||
- `centered`: When set to "true", adds flexbox classes to center the content both horizontally and vertically. Set to "false" by default.
|
||||
- **Inner Content**:
|
||||
- The shortcode accepts markdown-formatted inner content that will be rendered in the text section.
|
||||
- `sectionId`: Optional. Overrides the default HTML id for the section. If not provided, the default id is used.
|
||||
|
||||
- `spacer`:
|
||||
- **Size Options**:
|
||||
- `size`: Controls the amount of vertical spacing. Accepts "small", "medium" (default), "large", or "xlarge".
|
||||
- **Usage Examples**:
|
||||
- `{{</* spacer */>}}`: Adds medium spacing (default)
|
||||
- `{{</* spacer size="small" */>}}`: Adds minimal spacing
|
||||
- `{{</* spacer size="large" */>}}`: Adds substantial spacing
|
||||
- `{{</* spacer size="xlarge" */>}}`: Adds maximum spacing
|
||||
- `sectionId`: Optional. Overrides the default HTML id for the section. If not provided, the default id is used.
|
||||
|
||||
You can see them in effect in:
|
||||
- [the homepage](/) [`(see source)`](https://raw.githubusercontent.com/zetxek/adritian-demo/refs/heads/main/content/home.md).
|
||||
- [the CV page](/cv) [`(see source)`](https://raw.githubusercontent.com/zetxek/adritian-demo/refs/heads/main/content/cv.md).
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
date: '2024-08-25T10:21:48+02:00' # date in which the content is created - defaults to "today"
|
||||
title: 'Goldline'
|
||||
draft: false # set to "true" if you want to hide the content
|
||||
|
||||
link: "https://www.adrianmoreno.info" # optional URL to link the logo to
|
||||
|
||||
params:
|
||||
logo:
|
||||
src: "images/clients/goldline.png"
|
||||
scale: 0.5
|
||||
## The content is not used (yet). If you have ideas on how to use it,
|
||||
## you can suggest it at https://github.com/zetxek/adritian-free-hugo-theme/discussions
|
||||
---
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
date: '2024-08-25T10:22:34+02:00' # date in which the content is created - defaults to "today"
|
||||
title: 'Kanba'
|
||||
draft: false # set to "true" if you want to hide the content
|
||||
|
||||
link: "https://www.adrianmoreno.info" # optional URL to link the logo to
|
||||
|
||||
params:
|
||||
logo:
|
||||
src: "images/clients/kanba.png"
|
||||
scale: 0.5
|
||||
|
||||
## The content is not used (yet). If you have ideas on how to use it,
|
||||
## you can suggest it at https://github.com/zetxek/adritian-free-hugo-theme/discussions
|
||||
---
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
date: '2024-08-25T10:23:08+02:00' # date in which the content is created - defaults to "today"
|
||||
title: 'Zoo'
|
||||
draft: false # set to "true" if you want to hide the content
|
||||
|
||||
link: "https://www.adrianmoreno.info" # optional URL to link the logo to
|
||||
|
||||
params:
|
||||
logo:
|
||||
src: "images/clients/zoo-tv.png"
|
||||
scale: 0.5
|
||||
|
||||
## The content is not used (yet). If you have ideas on how to use it,
|
||||
## you can suggest it at https://github.com/zetxek/adritian-free-hugo-theme/discussions
|
||||
---
|
||||
@@ -0,0 +1,16 @@
|
||||
---
|
||||
date: '2024-08-25T09:53:42+02:00' # date in which the content is created - defaults to "today"
|
||||
title: 'Asgardia'
|
||||
draft: false # set to "true" if you want to hide the content
|
||||
|
||||
link: "https://www.adrianmoreno.info" # optional URL
|
||||
|
||||
params:
|
||||
logo:
|
||||
src: "images/clients/asgardia.png"
|
||||
scale: 0.5
|
||||
|
||||
|
||||
## The content is not used (yet). If you have ideas on how to use it,
|
||||
## you can suggest it at https://github.com/zetxek/adritian-free-hugo-theme/discussions
|
||||
---
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
date: '2024-08-25T10:17:12+02:00' # date in which the content is created - defaults to "today"
|
||||
title: 'Earth 2.0'
|
||||
draft: false # set to "true" if you want to hide the content
|
||||
|
||||
link: "https://www.adrianmoreno.info" # optional URL to link the logo to
|
||||
|
||||
params:
|
||||
logo:
|
||||
src: "images/clients/earth-2.png"
|
||||
scale: 0.5
|
||||
## The content is not used (yet). If you have ideas on how to use it,
|
||||
## you can suggest it at https://github.com/zetxek/adritian-free-hugo-theme/discussions
|
||||
---
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
date: '2024-08-25T10:23:39+02:00' # date in which the content is created - defaults to "today"
|
||||
title: 'Ztos'
|
||||
draft: false # set to "true" if you want to hide the content
|
||||
|
||||
link: "https://www.adrianmoreno.info" # optional URL to link the logo to
|
||||
|
||||
params:
|
||||
logo:
|
||||
src: "images/clients/ztos.png"
|
||||
scale: 0.5
|
||||
|
||||
## The content is not used (yet). If you have ideas on how to use it,
|
||||
## you can suggest it at https://github.com/zetxek/adritian-free-hugo-theme/discussions
|
||||
---
|
||||
@@ -0,0 +1,19 @@
|
||||
---
|
||||
title: 'Printable CV'
|
||||
hideTitle: true
|
||||
---
|
||||
|
||||
ℹ This page is a demo of the printer-friendly output of the theme. You can find the source code in the demo repo (https://raw.githubusercontent.com/zetxek/adritian-demo/refs/heads/main/content/cv.md). Try printing it directly from your browser!
|
||||
|
||||
### About me
|
||||
|
||||
I am a passionate software engineer with over 10 years of experience building web applications and distributed systems. My expertise spans full-stack development, cloud architecture, and leading engineering teams to deliver impactful solutions. I thrive on solving complex technical challenges while mentoring others and fostering a collaborative engineering culture.
|
||||
|
||||
Throughout my career, I've focused on creating scalable, maintainable software using modern technologies and best practices. I'm particularly interested in distributed systems, performance optimization, and building resilient architectures. When I'm not coding, I enjoy contributing to open source projects and sharing knowledge through technical writing and speaking at conferences.
|
||||
|
||||
### Experience
|
||||
|
||||
{{< experience-list >}}
|
||||
|
||||
{{< education-list title="Education" >}}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
date: '2024-08-25T09:53:42+02:00' # date in which the content is created - defaults to "today"
|
||||
title: 'High School of Hard Knocks'
|
||||
draft: false # set to "true" if you want to hide the content
|
||||
|
||||
university: "High School of Hard Knocks"
|
||||
year: "2009-2012"
|
||||
degree: "High School Diploma, Sciences, Math, Computer & Business Studies"
|
||||
|
||||
---
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
date: '2024-08-25T09:53:42+02:00' # date in which the content is created - defaults to "today"
|
||||
title: 'University of Life'
|
||||
draft: false # set to "true" if you want to hide the content
|
||||
|
||||
university: "University of Life"
|
||||
year: "2012-2017"
|
||||
degree: "Bachelor of Applied Science (BASc), Electrical Engineering"
|
||||
|
||||
---
|
||||
@@ -0,0 +1,12 @@
|
||||
---
|
||||
title: Experiencia
|
||||
---
|
||||
|
||||
Aquí es donde puedes destacar un poco sobre tu experiencia. Años de experiencia total, especialización, etc.
|
||||
|
||||
El contenido en esta introducción viene del archivo `content/experience/_index.md`.
|
||||
Este tipo de contenido, las "páginas índice", se denominan "branch bundles" (paquetes de rama). Puede leer más sobre [**secciones y paquetes** en la documentación de Hugo](https://gohugo.io/content-management/sections/#template-selection).
|
||||
|
||||
El contenido para cada elemento de experiencia (en los que puedes hacer clic a la izquierda) se define en la carpeta `content/experience`, con un elemento de contenido por experiencia, como en `job-1.es.md`, `job-2.es.md`, etc.
|
||||
|
||||
El contenido (texto y URL) para los botones a continuación (donde puedes agregar enlaces) proviene del archivo de traducción, `i18n/en.yaml`, en los elementos `experience_button`, `experience_button_url`, `experience_button2`, `experience_button2_url`, `experience_button3`, `experience_button3_url`.
|
||||