mirror of
https://github.com/imfing/hextra.git
synced 2025-07-02 02:27:22 -04:00
Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
c799160e86 | |||
00d26dee2c | |||
e9ea9786e9 | |||
84ac7fe773 | |||
a184cfd41e |
@ -2515,6 +2515,33 @@ nav .search-wrapper {
|
|||||||
.hamburger-menu svg.open > g:nth-of-type(2) path {
|
.hamburger-menu svg.open > g:nth-of-type(2) path {
|
||||||
transform: translate3d(0, -4px, 0);
|
transform: translate3d(0, -4px, 0);
|
||||||
}
|
}
|
||||||
|
.hextra-scrollbar {
|
||||||
|
scrollbar-width: thin; /* Firefox */
|
||||||
|
scrollbar-color: oklch(55.55% 0 0 / 40%) transparent; /* Firefox */
|
||||||
|
|
||||||
|
scrollbar-gutter: stable;
|
||||||
|
}
|
||||||
|
.hextra-scrollbar::-webkit-scrollbar {
|
||||||
|
height: 0.75rem;
|
||||||
|
width: 0.75rem;
|
||||||
|
}
|
||||||
|
.hextra-scrollbar::-webkit-scrollbar-track {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
.hextra-scrollbar::-webkit-scrollbar-thumb {
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
.hextra-scrollbar:hover::-webkit-scrollbar-thumb {
|
||||||
|
border: 3px solid transparent;
|
||||||
|
background-color: var(--tw-shadow-color);
|
||||||
|
background-clip: content-box;
|
||||||
|
--tw-shadow-color: rgb(115 115 115 / 0.2);
|
||||||
|
--tw-shadow: var(--tw-shadow-colored);
|
||||||
|
}
|
||||||
|
.hextra-scrollbar:hover::-webkit-scrollbar-thumb:hover {
|
||||||
|
--tw-shadow-color: rgb(115 115 115 / 0.4);
|
||||||
|
--tw-shadow: var(--tw-shadow-colored);
|
||||||
|
}
|
||||||
html {
|
html {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
|
21
assets/css/components/scrollbar.css
Normal file
21
assets/css/components/scrollbar.css
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
.hextra-scrollbar {
|
||||||
|
scrollbar-width: thin; /* Firefox */
|
||||||
|
scrollbar-color: oklch(55.55% 0 0 / 40%) transparent; /* Firefox */
|
||||||
|
|
||||||
|
scrollbar-gutter: stable;
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
@apply w-3 h-3;
|
||||||
|
}
|
||||||
|
&::-webkit-scrollbar-track {
|
||||||
|
@apply bg-transparent;
|
||||||
|
}
|
||||||
|
&::-webkit-scrollbar-thumb {
|
||||||
|
@apply rounded-[10px];
|
||||||
|
}
|
||||||
|
&:hover::-webkit-scrollbar-thumb {
|
||||||
|
border: 3px solid transparent;
|
||||||
|
background-color: var(--tw-shadow-color);
|
||||||
|
background-clip: content-box;
|
||||||
|
@apply shadow-neutral-500/20 hover:shadow-neutral-500/40;
|
||||||
|
}
|
||||||
|
}
|
@ -7,6 +7,7 @@
|
|||||||
@import "components/search.css";
|
@import "components/search.css";
|
||||||
@import "components/sidebar.css";
|
@import "components/sidebar.css";
|
||||||
@import "components/navbar.css";
|
@import "components/navbar.css";
|
||||||
|
@import "components/scrollbar.css";
|
||||||
|
|
||||||
html {
|
html {
|
||||||
@apply text-base antialiased;
|
@apply text-base antialiased;
|
||||||
|
@ -17,17 +17,21 @@ themeToggleButtons.forEach((el) => {
|
|||||||
if (localStorage.getItem("color-theme")) {
|
if (localStorage.getItem("color-theme")) {
|
||||||
if (localStorage.getItem("color-theme") === "light") {
|
if (localStorage.getItem("color-theme") === "light") {
|
||||||
document.documentElement.classList.add("dark");
|
document.documentElement.classList.add("dark");
|
||||||
|
document.documentElement.style.colorScheme = "dark";
|
||||||
localStorage.setItem("color-theme", "dark");
|
localStorage.setItem("color-theme", "dark");
|
||||||
} else {
|
} else {
|
||||||
document.documentElement.classList.remove("dark");
|
document.documentElement.classList.remove("dark");
|
||||||
|
document.documentElement.style.colorScheme = "light";
|
||||||
localStorage.setItem("color-theme", "light");
|
localStorage.setItem("color-theme", "light");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (document.documentElement.classList.contains("dark")) {
|
if (document.documentElement.classList.contains("dark")) {
|
||||||
document.documentElement.classList.remove("dark");
|
document.documentElement.classList.remove("dark");
|
||||||
|
document.documentElement.style.colorScheme = "light";
|
||||||
localStorage.setItem("color-theme", "light");
|
localStorage.setItem("color-theme", "light");
|
||||||
} else {
|
} else {
|
||||||
document.documentElement.classList.add("dark");
|
document.documentElement.classList.add("dark");
|
||||||
|
document.documentElement.style.colorScheme = "dark";
|
||||||
localStorage.setItem("color-theme", "dark");
|
localStorage.setItem("color-theme", "dark");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
BIN
exampleSite/assets/images/space.jpg
Normal file
BIN
exampleSite/assets/images/space.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 168 KiB |
@ -10,7 +10,11 @@ This page describes the available options and how to customize the theme further
|
|||||||
|
|
||||||
## Custom CSS
|
## Custom CSS
|
||||||
|
|
||||||
To add custom CSS, we need to create a file `assets/css/custom.css` in our site. Hextra will automatically load this file. For example, customize the font family of the content:
|
To add custom CSS, we need to create a file `assets/css/custom.css` in our site. Hextra will automatically load this file.
|
||||||
|
|
||||||
|
### Font Family
|
||||||
|
|
||||||
|
The font family of the content can be customized using:
|
||||||
|
|
||||||
```css {filename="assets/css/custom.css"}
|
```css {filename="assets/css/custom.css"}
|
||||||
.content {
|
.content {
|
||||||
@ -18,6 +22,16 @@ To add custom CSS, we need to create a file `assets/css/custom.css` in our site.
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Inline Code Element
|
||||||
|
|
||||||
|
The color of text mixed with `other text` can customized with:
|
||||||
|
|
||||||
|
```css {filename="assets/css/custom.css"}
|
||||||
|
.content code:not(.code-block code) {
|
||||||
|
color: #c97c2e;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### Primary Color
|
### Primary Color
|
||||||
|
|
||||||
The primary color of the theme can be customized by setting the `--primary-hue` variable:
|
The primary color of the theme can be customized by setting the `--primary-hue` variable:
|
||||||
|
@ -12,9 +12,10 @@ linkTitle: Cards
|
|||||||
|
|
||||||
{{< cards >}}
|
{{< cards >}}
|
||||||
{{< card link="/" title="Image Card" image="https://source.unsplash.com/featured/800x600?landscape" subtitle="Unsplash Landscape" >}}
|
{{< card link="/" title="Image Card" image="https://source.unsplash.com/featured/800x600?landscape" subtitle="Unsplash Landscape" >}}
|
||||||
|
{{< card link="/" title="Local Image" image="/images/space.jpg" subtitle="Image under assets directory, processed by Hugo." method="Resize" options="600x q80 webp" >}}
|
||||||
|
{{< card link="/" title="Local Image" image="/images/card-image-unprocessed.jpg" subtitle="Raw image under static directory." >}}
|
||||||
{{< /cards >}}
|
{{< /cards >}}
|
||||||
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
```
|
```
|
||||||
@ -27,5 +28,37 @@ linkTitle: Cards
|
|||||||
```
|
```
|
||||||
{{</* cards */>}}
|
{{</* cards */>}}
|
||||||
{{</* card link="/" title="Image Card" image="https://source.unsplash.com/featured/800x600?landscape" subtitle="Unsplash Landscape" */>}}
|
{{</* card link="/" title="Image Card" image="https://source.unsplash.com/featured/800x600?landscape" subtitle="Unsplash Landscape" */>}}
|
||||||
|
{{</* card link="/" title="Local Image" image="/images/space.jpg" subtitle="Image under assets directory, processed by Hugo." method="Resize" options="600x q80 webp" */>}}
|
||||||
|
{{</* card link="/" title="Local Image" image="/images/card-image-unprocessed.jpg" subtitle="Raw image under static directory." */>}}
|
||||||
{{</* /cards */>}}
|
{{</* /cards */>}}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Card Parameters
|
||||||
|
|
||||||
|
| Parameter | Description |
|
||||||
|
|----------- |---------------------------------------|
|
||||||
|
| `link` | URL (internal or external). |
|
||||||
|
| `title` | Title heading for the card. |
|
||||||
|
| `subtitle` | Subtitle heading (supports Markdown). |
|
||||||
|
| `icon` | Name of the icon. |
|
||||||
|
|
||||||
|
## Image Card
|
||||||
|
|
||||||
|
Additionally, the card supports adding image and processing through these parameters:
|
||||||
|
|
||||||
|
| Parameter | Description |
|
||||||
|
|----------- |---------------------------------------------|
|
||||||
|
| `image` | Specifies the image URL for the card. |
|
||||||
|
| `method` | Sets Hugo's image processing method. |
|
||||||
|
| `options` | Configures Hugo's image processing options. |
|
||||||
|
|
||||||
|
Card supports three kinds of images:
|
||||||
|
|
||||||
|
1. Remote image: the full URL in the `image` parameter.
|
||||||
|
2. Static image: use the relative path in Hugo's `static/` directory.
|
||||||
|
3. Processed image: use the relative path in Hugo's `assets/` directory.
|
||||||
|
|
||||||
|
Hextra auto-detects if image processing is needed during build and applies the `options` parameter or default settings (Resize, 800x, Quality 80, WebP Format).
|
||||||
|
It currently supports these `method`: `Resize`, `Fit`, `Fill` and `Crop`.
|
||||||
|
|
||||||
|
For more on Hugo's built in image processing commands, methods, and options see their [Image Processing Documentation](https://gohugo.io/content-management/image-processing/).
|
||||||
|
@ -277,6 +277,7 @@
|
|||||||
"hextra-filetree",
|
"hextra-filetree",
|
||||||
"hextra-filetree-folder",
|
"hextra-filetree-folder",
|
||||||
"hextra-footer",
|
"hextra-footer",
|
||||||
|
"hextra-scrollbar",
|
||||||
"hextra-sidebar-collapsible-button",
|
"hextra-sidebar-collapsible-button",
|
||||||
"hextra-toc",
|
"hextra-toc",
|
||||||
"hidden",
|
"hidden",
|
||||||
@ -580,4 +581,4 @@
|
|||||||
],
|
],
|
||||||
"ids": null
|
"ids": null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
BIN
exampleSite/static/images/card-image-unprocessed.jpg
Normal file
BIN
exampleSite/static/images/card-image-unprocessed.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 187 KiB |
@ -24,8 +24,10 @@
|
|||||||
/* Initialize light/dark mode */
|
/* Initialize light/dark mode */
|
||||||
if (localStorage.getItem("color-theme") === "dark" || (!("color-theme" in localStorage) && window.matchMedia("(prefers-color-scheme: dark)").matches)) {
|
if (localStorage.getItem("color-theme") === "dark" || (!("color-theme" in localStorage) && window.matchMedia("(prefers-color-scheme: dark)").matches)) {
|
||||||
document.documentElement.classList.add("dark");
|
document.documentElement.classList.add("dark");
|
||||||
|
document.documentElement.style.colorScheme = "dark";
|
||||||
} else {
|
} else {
|
||||||
document.documentElement.classList.remove("dark");
|
document.documentElement.classList.remove("dark");
|
||||||
|
document.documentElement.style.colorScheme = "light";
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -11,8 +11,8 @@
|
|||||||
<nav class="mx-auto flex items-center justify-end gap-2 h-16 px-6 max-w-[90rem]">
|
<nav class="mx-auto flex items-center justify-end gap-2 h-16 px-6 max-w-[90rem]">
|
||||||
<a class="flex items-center hover:opacity-75 ltr:mr-auto rtl:ml-auto" href="{{ $logoLink }}">
|
<a class="flex items-center hover:opacity-75 ltr:mr-auto rtl:ml-auto" href="{{ $logoLink }}">
|
||||||
{{- if (.Site.Params.navbar.displayLogo | default true) }}
|
{{- if (.Site.Params.navbar.displayLogo | default true) }}
|
||||||
<img class="block dark:hidden" src="{{ $logoPath | relURL }}" alt="{{ .Site.Title }}" height="{{ $logoWidth }}" width="{{ $logoHeight }}" />
|
<img class="block dark:hidden" src="{{ $logoPath | relURL }}" alt="{{ .Site.Title }}" height="{{ $logoHeight }}" width="{{ $logoWidth }}" />
|
||||||
<img class="hidden dark:block" src="{{ $logoDarkPath | relURL }}" alt="{{ .Site.Title }}" height="{{ $logoWidth }}" width="{{ $logoHeight }}" />
|
<img class="hidden dark:block" src="{{ $logoDarkPath | relURL }}" alt="{{ .Site.Title }}" height="{{ $logoHeight }}" width="{{ $logoWidth }}" />
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if (.Site.Params.navbar.displayTitle | default true) }}
|
{{- if (.Site.Params.navbar.displayTitle | default true) }}
|
||||||
<span class="mx-2 font-extrabold inline select-none" title="{{ .Site.Title }}">{{- .Site.Title -}}</span>
|
<span class="mx-2 font-extrabold inline select-none" title="{{ .Site.Title }}">{{- .Site.Title -}}</span>
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
<ul
|
<ul
|
||||||
class="search-results hidden border border-gray-200 bg-white text-gray-100 dark:border-neutral-800 dark:bg-neutral-900 absolute top-full z-20 mt-2 overflow-auto overscroll-contain rounded-xl py-2.5 shadow-xl max-h-[min(calc(50vh-11rem-env(safe-area-inset-bottom)),400px)] md:max-h-[min(calc(100vh-5rem-env(safe-area-inset-bottom)),400px)] inset-x-0 ltr:md:left-auto rtl:md:right-auto contrast-more:border contrast-more:border-gray-900 contrast-more:dark:border-gray-50 w-screen min-h-[100px] max-w-[min(calc(100vw-2rem),calc(100%+20rem))]"
|
class="search-results hextra-scrollbar hidden border border-gray-200 bg-white text-gray-100 dark:border-neutral-800 dark:bg-neutral-900 absolute top-full z-20 mt-2 overflow-auto overscroll-contain rounded-xl py-2.5 shadow-xl max-h-[min(calc(50vh-11rem-env(safe-area-inset-bottom)),400px)] md:max-h-[min(calc(100vh-5rem-env(safe-area-inset-bottom)),400px)] inset-x-0 ltr:md:left-auto rtl:md:right-auto contrast-more:border contrast-more:border-gray-900 contrast-more:dark:border-gray-50 w-screen min-h-[100px] max-w-[min(calc(100vw-2rem),calc(100%+20rem))]"
|
||||||
style="transition: max-height 0.2s ease 0s;"
|
style="transition: max-height 0.2s ease 0s;"
|
||||||
></ul>
|
></ul>
|
||||||
</div>
|
</div>
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
<div class="px-4 pt-4 md:hidden">
|
<div class="px-4 pt-4 md:hidden">
|
||||||
{{ partial "search.html" }}
|
{{ partial "search.html" }}
|
||||||
</div>
|
</div>
|
||||||
<div class="overflow-y-auto overflow-x-hidden p-4 grow md:h-[calc(100vh-var(--navbar-height)-var(--menu-height))]">
|
<div class="hextra-scrollbar overflow-y-auto overflow-x-hidden p-4 grow md:h-[calc(100vh-var(--navbar-height)-var(--menu-height))]">
|
||||||
<ul class="flex flex-col gap-1 md:hidden">
|
<ul class="flex flex-col gap-1 md:hidden">
|
||||||
<!-- Nav -->
|
<!-- Nav -->
|
||||||
{{ template "sidebar-main" (dict "context" site.Home "pageURL" $pageURL "page" $context "toc" true) -}}
|
{{ template "sidebar-main" (dict "context" site.Home "pageURL" $pageURL "page" $context "toc" true) -}}
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<nav class="hextra-toc order-last hidden w-64 shrink-0 xl:block print:hidden px-4" aria-label="table of contents">
|
<nav class="hextra-toc order-last hidden w-64 shrink-0 xl:block print:hidden px-4" aria-label="table of contents">
|
||||||
{{- if $toc }}
|
{{- if $toc }}
|
||||||
<div class="sticky top-16 overflow-y-auto pr-4 pt-6 text-sm [hyphens:auto] max-h-[calc(100vh-var(--navbar-height)-env(safe-area-inset-bottom))] ltr:-mr-4 rtl:-ml-4">
|
<div class="hextra-scrollbar sticky top-16 overflow-y-auto pr-4 pt-6 text-sm [hyphens:auto] max-h-[calc(100vh-var(--navbar-height)-env(safe-area-inset-bottom))] ltr:-mr-4 rtl:-ml-4">
|
||||||
{{- with .Fragments.Headings -}}
|
{{- with .Fragments.Headings -}}
|
||||||
<p class="mb-4 font-semibold tracking-tight">{{ $onThisPage }}</p>
|
<p class="mb-4 font-semibold tracking-tight">{{ $onThisPage }}</p>
|
||||||
{{- range . -}}
|
{{- range . -}}
|
||||||
|
@ -1,21 +1,41 @@
|
|||||||
{{- $link := .Get "link" -}}
|
{{- $link := .Get "link" -}}
|
||||||
{{- $title := .Get "title" -}}
|
{{- $title := .Get "title" -}}
|
||||||
{{- $icon := .Get "icon" -}}
|
{{- $icon := .Get "icon" -}}
|
||||||
{{- $subtitle := .Get "subtitle" }}
|
{{- $subtitle := .Get "subtitle" -}}
|
||||||
{{- $image := .Get "image" }}
|
{{- $image := .Get "image" -}}
|
||||||
|
{{- $method := .Get "method" | default "Resize" | humanize -}}
|
||||||
|
{{- $options := .Get "options" | default "800x webp q80" -}}
|
||||||
{{- $context := . -}}
|
{{- $context := . -}}
|
||||||
|
|
||||||
|
|
||||||
|
{{/*- Adding asset support for images here, so that Hugo can do its image processing magic. -*/}}
|
||||||
|
{{/* Unfortunately we cannot pass .Resize/.Fit/.Fill as variables, so we're left with chaining IFs */}}
|
||||||
|
|
||||||
|
{{- if not (urls.Parse $image).Scheme -}}
|
||||||
|
{{- with resources.Get $image -}}
|
||||||
|
{{- if eq $method "Resize" -}}
|
||||||
|
{{- $image = (.Resize $options).RelPermalink -}}
|
||||||
|
{{- else if eq $method "Fit" -}}
|
||||||
|
{{- $image = (.Fit $options).RelPermalink -}}
|
||||||
|
{{- else if eq $method "Fill" -}}
|
||||||
|
{{- $image = (.Fill $options).RelPermalink -}}
|
||||||
|
{{- else if eq $method "Crop" -}}
|
||||||
|
{{- $image = (.Crop $options).RelPermalink -}}
|
||||||
|
{{- end -}}
|
||||||
|
{{- end -}}
|
||||||
|
{{- end -}}
|
||||||
|
|
||||||
{{ $linkClass := "hover:border-gray-300 bg-transparent shadow-sm dark:border-neutral-800 hover:bg-slate-50 hover:shadow-md dark:hover:border-neutral-700 dark:hover:bg-neutral-900" }}
|
{{ $linkClass := "hover:border-gray-300 bg-transparent shadow-sm dark:border-neutral-800 hover:bg-slate-50 hover:shadow-md dark:hover:border-neutral-700 dark:hover:bg-neutral-900" }}
|
||||||
{{- with $image -}}
|
{{- with $image -}}
|
||||||
{{ $linkClass = "hover:border-gray-300 bg-gray-100 shadow dark:border-neutral-700 dark:bg-neutral-800 dark:text-gray-50 hover:shadow-lg dark:hover:border-neutral-500 dark:hover:bg-neutral-700" }}
|
{{ $linkClass = "hover:border-gray-300 bg-gray-100 shadow dark:border-neutral-700 dark:bg-neutral-800 dark:text-gray-50 hover:shadow-lg dark:hover:border-neutral-500 dark:hover:bg-neutral-700" }}
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
|
|
||||||
{{- $external := strings.HasPrefix $link "http" -}}
|
{{- $external := strings.HasPrefix $link "http" -}}
|
||||||
|
{{- $href := cond (strings.HasPrefix $link "/") ($link | relURL) $link -}}
|
||||||
|
|
||||||
<a
|
<a
|
||||||
class="hextra-card group flex flex-col justify-start overflow-hidden rounded-lg border border-gray-200 text-current no-underline dark:shadow-none hover:shadow-gray-100 dark:hover:shadow-none shadow-gray-100 active:shadow-sm active:shadow-gray-200 transition-all duration-200 {{ $linkClass }}"
|
class="hextra-card group flex flex-col justify-start overflow-hidden rounded-lg border border-gray-200 text-current no-underline dark:shadow-none hover:shadow-gray-100 dark:hover:shadow-none shadow-gray-100 active:shadow-sm active:shadow-gray-200 transition-all duration-200 {{ $linkClass }}"
|
||||||
href="{{ $link | relURL }}"
|
href="{{ $href }}"
|
||||||
{{- if $external }}
|
{{- if $external }}
|
||||||
target="_blank" rel="noreferrer"
|
target="_blank" rel="noreferrer"
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
@ -31,10 +51,10 @@
|
|||||||
|
|
||||||
|
|
||||||
<span class="flex font-semibold items-start gap-2 {{ $padding }} text-gray-700 hover:text-gray-900 dark:text-neutral-200 dark:hover:text-neutral-50">
|
<span class="flex font-semibold items-start gap-2 {{ $padding }} text-gray-700 hover:text-gray-900 dark:text-neutral-200 dark:hover:text-neutral-50">
|
||||||
{{- with $icon }}{{ partial "utils/icon.html" (dict "name" $icon) -}}{{ end -}}
|
{{- with $icon }}{{ partial "utils/icon.html" (dict "name" $icon) -}}{{- end -}}
|
||||||
{{- $title -}}
|
{{- $title -}}
|
||||||
</span>
|
</span>
|
||||||
{{- with $subtitle -}}
|
{{- with $subtitle -}}
|
||||||
<div class="line-clamp-3 text-sm font-normal text-gray-500 dark:text-gray-400 px-4 mb-4 mt-2">{{ $subtitle }}</div>
|
<div class="line-clamp-3 text-sm font-normal text-gray-500 dark:text-gray-400 px-4 mb-4 mt-2">{{- $subtitle | markdownify -}}</div>
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
</a>
|
</a>
|
||||||
|
Reference in New Issue
Block a user