Merge branch 'master' into production

This commit is contained in:
Cotes Chung 2024-11-28 16:56:02 +08:00
commit 82ba82e6da
No known key found for this signature in database
GPG key ID: 0D9E54843167A808
95 changed files with 3497 additions and 2951 deletions

View file

@ -2,13 +2,12 @@ name: CD
on: on:
push: push:
branches: branches: [production]
- production tags-ignore: ["**"]
tags-ignore:
- "**"
jobs: jobs:
release: release:
if: ${{ ! startsWith(github.event.head_commit.message, 'chore(release)') }}
permissions: permissions:
contents: write contents: write
issues: write issues: write

View file

@ -1,17 +1,25 @@
name: "CI" name: CI
on: on:
push: push:
branches: branches:
- "master" - master
- "hotfix/**" - "hotfix/*"
paths-ignore: paths-ignore:
- ".github/**" - ".github/**"
- "!.github/workflows/ci.yml" - "!.github/workflows/ci.yml"
- ".gitignore" - .gitignore
- "docs/**" - "docs/**"
- "README.md" - README.md
- "LICENSE" - LICENSE
pull_request: pull_request:
paths-ignore:
- ".github/**"
- "!.github/workflows/ci.yml"
- .gitignore
- "docs/**"
- README.md
- LICENSE
jobs: jobs:
build: build:

View file

@ -1,5 +1,11 @@
name: Lint Commit Messages name: Lint Commit Messages
on: pull_request
on:
push:
branches:
- master
- "hotfix/*"
pull_request:
jobs: jobs:
commitlint: commitlint:

25
.github/workflows/pr-filter.yml vendored Normal file
View file

@ -0,0 +1,25 @@
name: PR Filter
on:
pull_request_target:
types: [opened, reopened]
jobs:
check-template:
if: github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Check PR Content
id: intercept
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const script = require('.github/workflows/scripts/pr-filter.js');
await script({ github, context, core });

View file

@ -10,6 +10,7 @@ on:
required: true required: true
BUILDER: BUILDER:
required: true required: true
workflow_dispatch:
jobs: jobs:
launch: launch:

36
.github/workflows/scripts/pr-filter.js vendored Normal file
View file

@ -0,0 +1,36 @@
function hasTypes(markdown) {
return /## Type of change/.test(markdown) && /-\s\[x\]/i.test(markdown);
}
function hasDescription(markdown) {
return (
/## Description/.test(markdown) &&
!/## Description\s*\n\s*(##|\s*$)/.test(markdown)
);
}
module.exports = async ({ github, context, core }) => {
const pr = context.payload.pull_request;
const body = pr.body === null ? '' : pr.body;
const markdown = body.replace(/<!--[\s\S]*?-->/g, '');
const action = context.payload.action;
const isValid =
markdown !== '' && hasTypes(markdown) && hasDescription(markdown);
if (!isValid) {
await github.rest.pulls.update({
...context.repo,
pull_number: pr.number,
state: 'closed'
});
await github.rest.issues.createComment({
...context.repo,
issue_number: pr.number,
body: `Oops, it seems you've ${action} an invalid pull request. No worries, we'll close it for you.`
});
core.setFailed('PR content does not meet template requirements.');
}
};

View file

@ -9,7 +9,7 @@ permissions:
pull-requests: write pull-requests: write
env: env:
STALE_LABEL: stale STALE_LABEL: inactive
EXEMPT_LABELS: "pending,planning,in progress" EXEMPT_LABELS: "pending,planning,in progress"
MESSAGE: > MESSAGE: >
This conversation has been automatically marked as stale because it has not had recent activity. This conversation has been automatically marked as stale because it has not had recent activity.

View file

@ -1,23 +0,0 @@
name: "Style Lint"
on:
push:
branches: ["master", "hotfix/**"]
paths: ["_sass/**/*.scss"]
pull_request:
paths: ["_sass/**/*.scss"]
jobs:
stylelint:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: lts/*
- run: npm i
- run: npm test

2
.gitignore vendored
View file

@ -23,5 +23,5 @@ package-lock.json
!.vscode/tasks.json !.vscode/tasks.json
# Misc # Misc
_sass/dist _sass/vendors
assets/js/dist assets/js/dist

31
.stylelintrc.json Normal file
View file

@ -0,0 +1,31 @@
{
"ignoreFiles": ["_sass/vendors/**"],
"extends": "stylelint-config-standard-scss",
"rules": {
"no-descending-specificity": null,
"shorthand-property-no-redundant-values": null,
"at-rule-no-vendor-prefix": null,
"property-no-vendor-prefix": null,
"selector-no-vendor-prefix": null,
"value-no-vendor-prefix": null,
"color-function-notation": "legacy",
"alpha-value-notation": "number",
"selector-not-notation": "simple",
"color-hex-length": "long",
"declaration-block-single-line-max-declarations": 3,
"scss/operator-no-newline-after": null,
"rule-empty-line-before": [
"always",
{
"ignore": ["after-comment", "first-nested"]
}
],
"value-keyword-case": [
"lower",
{
"ignoreProperties": ["/^\\$/"]
}
],
"media-feature-range-notation": "prefix"
}
}

View file

@ -11,4 +11,4 @@ platforms :mingw, :x64_mingw, :mswin, :jruby do
gem "tzinfo-data" gem "tzinfo-data"
end end
gem "wdm", "~> 0.1.1", :platforms => [:mingw, :x64_mingw, :mswin] gem "wdm", "~> 0.2.0", :platforms => [:mingw, :x64_mingw, :mswin]

View file

@ -14,24 +14,23 @@ tabs:
categories: Kategóriák categories: Kategóriák
tags: Címkék tags: Címkék
archives: Archívum archives: Archívum
about: Rólam about: Bemutatkozás
# the text displayed in the search bar & search results # the text displayed in the search bar & search results
search: search:
hint: keresés hint: keresés
cancel: Mégse cancel: Mégse
no_results: Oops! Nincs találat a keresésre. no_results: Hoppá! Nincs találat a keresésre.
panel: panel:
lastmod: Legutóbb frissítve lastmod: Legutóbb frissítve
trending_tags: Népszerű Címkék trending_tags: Népszerű Címkék
toc: Tartalom toc: Tartalom
links: Blog linkek
copyright: copyright:
# Shown at the bottom of the post # Shown at the bottom of the post
license: license:
template: A bejegyzés :LICENSE_NAME licenccel rendelkezik. template: A bejegyzést a szerző :LICENSE_NAME licenc alatt engedélyezte.
name: CC BY 4.0 name: CC BY 4.0
link: https://creativecommons.org/licenses/by/4.0/ link: https://creativecommons.org/licenses/by/4.0/
@ -42,7 +41,7 @@ copyright:
Creative Commons Attribution 4.0 International (CC BY 4.0) licenccel rendelkeznek, Creative Commons Attribution 4.0 International (CC BY 4.0) licenccel rendelkeznek,
hacsak másképp nincs jelezve. hacsak másképp nincs jelezve.
meta: Készítve :PLATFORM motorral :THEME témával meta: Készítve :THEME témával a :PLATFORM platformra.
not_found: not_found:
statement: Sajnáljuk, az URL-t rosszul helyeztük el, vagy valami nem létezőre mutat. statement: Sajnáljuk, az URL-t rosszul helyeztük el, vagy valami nem létezőre mutat.
@ -73,7 +72,21 @@ post:
title: Link másolása title: Link másolása
succeed: Link sikeresen másolva! succeed: Link sikeresen másolva!
# Date time format.
# See: <http://strftime.net/>, <https://day.js.org/docs/en/display/format>
df:
post:
strftime: "%Y. %B. %e."
dayjs: "YYYY. MMMM D."
archives:
strftime: "%B"
dayjs: "MMM"
# categories page # categories page
categories: categories:
category_measure: kategória category_measure:
post_measure: bejegyzés singular: kategória
plural: kategória
post_measure:
singular: bejegyzés
plural: bejegyzés

View file

@ -20,17 +20,17 @@ webfonts: https://fonts.googleapis.com/css2?family=Lato:wght@300;400&family=Sour
# Libraries # Libraries
toc: toc:
css: https://cdn.jsdelivr.net/npm/tocbot@4.29.0/dist/tocbot.min.css css: https://cdn.jsdelivr.net/npm/tocbot@4.32.2/dist/tocbot.min.css
js: https://cdn.jsdelivr.net/npm/tocbot@4.29.0/dist/tocbot.min.js js: https://cdn.jsdelivr.net/npm/tocbot@4.32.2/dist/tocbot.min.js
fontawesome: fontawesome:
css: https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.6.0/css/all.min.css css: https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.7.1/css/all.min.css
search: search:
js: https://cdn.jsdelivr.net/npm/simple-jekyll-search@1.10.0/dest/simple-jekyll-search.min.js js: https://cdn.jsdelivr.net/npm/simple-jekyll-search@1.10.0/dest/simple-jekyll-search.min.js
mermaid: mermaid:
js: https://cdn.jsdelivr.net/npm/mermaid@11.0.2/dist/mermaid.min.js js: https://cdn.jsdelivr.net/npm/mermaid@11.4.0/dist/mermaid.min.js
dayjs: dayjs:
js: js:

View file

@ -4,4 +4,3 @@
src="https://static.cloudflareinsights.com/beacon.min.js" src="https://static.cloudflareinsights.com/beacon.min.js"
data-cf-beacon='{"token": "{{ site.analytics.cloudflare.id }}"}' data-cf-beacon='{"token": "{{ site.analytics.cloudflare.id }}"}'
></script> ></script>
<!-- End Cloudflare Web Analytics -->

View file

@ -2,6 +2,5 @@
<script <script
src="https://cdn.usefathom.com/script.js" src="https://cdn.usefathom.com/script.js"
data-site="{{ site.analytics.fathom.id }}" data-site="{{ site.analytics.fathom.id }}"
defer> defer
</script> ></script>
<!-- End Fathom Code -->

View file

@ -1,7 +1,7 @@
<!-- Global site tag (gtag.js) - Google Analytics --> <!-- Global site tag (gtag.js) - Google Analytics -->
<script defer src="https://www.googletagmanager.com/gtag/js?id={{ site.analytics.google.id }}"></script> <script defer src="https://www.googletagmanager.com/gtag/js?id={{ site.analytics.google.id }}"></script>
<script> <script>
document.addEventListener('DOMContentLoaded', function (event) { document.addEventListener('DOMContentLoaded', () => {
window.dataLayer = window.dataLayer || []; window.dataLayer = window.dataLayer || [];
function gtag() { function gtag() {
dataLayer.push(arguments); dataLayer.push(arguments);

View file

@ -1,14 +1,13 @@
<!-- Matomo --> <!-- Matomo -->
<script type="text/javascript"> <script>
var _paq = window._paq = window._paq || []; document.addEventListener('DOMContentLoaded', () => {
_paq.push(['trackPageView']); var _paq = (window._paq = window._paq || []);
_paq.push(['enableLinkTracking']); _paq.push(['trackPageView']);
(function() { _paq.push(['enableLinkTracking']);
var u="//{{ site.analytics.matomo.domain }}/"; var u="//{{ site.analytics.matomo.domain }}/";
_paq.push(['setTrackerUrl', u+'matomo.php']); _paq.push(['setTrackerUrl', u+'matomo.php']);
_paq.push(['setSiteId', {{ site.analytics.matomo.id }}]); _paq.push(['setSiteId', {{ site.analytics.matomo.id }}]);
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0]; var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
g.type='text/javascript'; g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s); g.type='text/javascript'; g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
})(); });
</script> </script>
<!-- End Matomo Code -->

View file

@ -1,38 +1,25 @@
<!-- The Disqus lazy loading. --> <script>
<div id="disqus_thread">
<p class="text-center text-muted small">Comments powered by <a href="https://disqus.com/">Disqus</a>.</p>
</div>
<script type="text/javascript">
var disqus_config = function () { var disqus_config = function () {
this.page.url = '{{ page.url | absolute_url }}'; this.page.url = '{{ page.url | absolute_url }}';
this.page.identifier = '{{ page.url }}'; this.page.identifier = '{{ page.url }}';
}; };
{%- comment -%} Lazy loading {%- endcomment -%} function addDisqus() {
var disqus_observer = new IntersectionObserver( let disqusThread = document.createElement('div');
function (entries) { let paragraph = document.createElement('p');
if (entries[0].isIntersecting) {
(function () {
var d = document,
s = d.createElement('script');
s.src = 'https://{{ site.comments.disqus.shortname }}.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
})();
disqus_observer.disconnect(); disqusThread.id = 'disqus_thread';
} paragraph.className = 'text-center text-muted small';
}, paragraph.innerHTML = 'Comments powered by <a href="https://disqus.com/">Disqus</a>.';
{ threshold: [0] } disqusThread.appendChild(paragraph);
);
disqus_observer.observe(document.getElementById('disqus_thread')); const footer = document.querySelector('footer');
footer.insertAdjacentElement("beforebegin", disqusThread);
}
{%- comment -%} Auto switch theme {%- endcomment -%} {%- comment -%} Auto switch theme {%- endcomment -%}
function reloadDisqus() { function reloadDisqus(event) {
if (event.source === window && event.data && event.data.direction === ModeToggle.ID) { if (event.source === window && event.data && event.data.id === Theme.ID) {
{%- comment -%} Disqus hasn't been loaded {%- endcomment -%} {%- comment -%} Disqus hasn't been loaded {%- endcomment -%}
if (typeof DISQUS === 'undefined') { if (typeof DISQUS === 'undefined') {
return; return;
@ -44,7 +31,27 @@
} }
} }
if (document.getElementById('mode-toggle')) { addDisqus();
window.addEventListener('message', reloadDisqus);
if (Theme.switchable) {
addEventListener('message', reloadDisqus);
} }
{%- comment -%} Lazy loading {%- endcomment -%}
var disqusObserver = new IntersectionObserver(
function (entries) {
if (entries[0].isIntersecting) {
var d = document,
s = d.createElement('script');
s.src = 'https://{{ site.comments.disqus.shortname }}.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
disqusObserver.disconnect();
}
},
{ threshold: [0] }
);
disqusObserver.observe(document.getElementById('disqus_thread'));
</script> </script>

View file

@ -1,21 +1,8 @@
<!-- https://giscus.app/ --> <!-- https://giscus.app/ -->
<script type="text/javascript"> <script>
(function () { (function () {
const origin = 'https://giscus.app'; const themeMapper = Theme.getThemeMapper('light', 'dark_dimmed');
const lightTheme = 'light'; const initTheme = themeMapper[Theme.visualState];
const darkTheme = 'dark_dimmed';
let initTheme = lightTheme;
const html = document.documentElement;
if (
(html.hasAttribute('data-mode') &&
html.getAttribute('data-mode') === 'dark') ||
(!html.hasAttribute('data-mode') &&
window.matchMedia('(prefers-color-scheme: dark)').matches)
) {
initTheme = darkTheme;
}
let lang = '{{ site.comments.giscus.lang | default: lang }}'; let lang = '{{ site.comments.giscus.lang | default: lang }}';
{%- comment -%} https://github.com/giscus/giscus/tree/main/locales {%- endcomment -%} {%- comment -%} https://github.com/giscus/giscus/tree/main/locales {%- endcomment -%}
@ -41,30 +28,27 @@
async: '' async: ''
}; };
let giscusScript = document.createElement('script'); let giscusNode = document.createElement('script');
Object.entries(giscusAttributes).forEach(([key, value]) => Object.entries(giscusAttributes).forEach(([key, value]) =>
giscusScript.setAttribute(key, value) giscusNode.setAttribute(key, value)
); );
document.getElementById('tail-wrapper').appendChild(giscusScript);
const $footer = document.querySelector('footer');
$footer.insertAdjacentElement("beforebegin", giscusNode);
addEventListener('message', (event) => { addEventListener('message', (event) => {
if ( if (event.source === window && event.data && event.data.id === Theme.ID) {
event.source === window && const newTheme = themeMapper[Theme.visualState];
event.data &&
event.data.direction === ModeToggle.ID
) {
{%- comment -%} global theme mode changed {%- endcomment -%}
const mode = event.data.message;
const theme = mode === ModeToggle.DARK_MODE ? darkTheme : lightTheme;
const message = { const message = {
setConfig: { setConfig: {
theme: theme theme: newTheme
} }
}; };
const giscus = document.getElementsByClassName('giscus-frame')[0].contentWindow; const giscus =
giscus.postMessage({ giscus: message }, origin); document.getElementsByClassName('giscus-frame')[0].contentWindow;
giscus.postMessage({ giscus: message }, 'https://giscus.app');
} }
}); });
})(); })();

View file

@ -1,49 +1,38 @@
<!-- https://utteranc.es/ --> <!-- https://utteranc.es/ -->
<script <script>
src="https://utteranc.es/client.js"
repo="{{ site.comments.utterances.repo }}"
issue-term="{{ site.comments.utterances.issue_term }}"
crossorigin="anonymous"
async
></script>
<script type="text/javascript">
(function () { (function () {
const origin = 'https://utteranc.es'; const origin = 'https://utteranc.es';
const lightTheme = 'github-light'; const themeMapper = Theme.getThemeMapper('github-light', 'github-dark');
const darkTheme = 'github-dark'; const initTheme = themeMapper[Theme.visualState];
let initTheme = lightTheme;
const html = document.documentElement;
if ( let script = document.createElement('script');
(html.hasAttribute('data-mode') && html.getAttribute('data-mode') === 'dark') || script.src = 'https://utteranc.es/client.js';
(!html.hasAttribute('data-mode') && window.matchMedia('(prefers-color-scheme: dark)').matches) script.setAttribute('repo', '{{ site.comments.utterances.repo }}');
) { script.setAttribute('issue-term', '{{ site.comments.utterances.issue_term }}');
initTheme = darkTheme; script.setAttribute('theme', initTheme);
} script.crossOrigin = 'anonymous';
script.async = true;
const $footer = document.querySelector('footer');
$footer.insertAdjacentElement('beforebegin', script);
addEventListener('message', (event) => { addEventListener('message', (event) => {
let theme; let newTheme;
{%- comment -%} credit to <https://github.com/utterance/utterances/issues/170#issuecomment-594036347> {%- endcomment -%} {%- comment -%}
if (event.origin === origin) { Credit to <https://github.com/utterance/utterances/issues/170#issuecomment-594036347>
{%- comment -%} page initial {%- endcomment -%} {%- endcomment -%}
theme = initTheme; if (event.source === window && event.data && event.data.id === Theme.ID) {
} else if (event.source === window && event.data && event.data.direction === ModeToggle.ID) { newTheme = themeMapper[Theme.visualState];
{%- comment -%} global theme mode changed {%- endcomment -%}
const mode = event.data.message; const message = {
theme = mode === ModeToggle.DARK_MODE ? darkTheme : lightTheme; type: 'set-theme',
} else { theme: newTheme
return; };
const utterances = document.querySelector('.utterances-frame').contentWindow;
utterances.postMessage(message, origin);
} }
const message = {
type: 'set-theme',
theme: theme
};
const utterances = document.getElementsByClassName('utterances-frame')[0].contentWindow;
utterances.postMessage(message, origin);
}); });
})(); })();
</script> </script>

View file

@ -70,7 +70,7 @@
<!-- Bootstrap --> <!-- Bootstrap -->
{% unless jekyll.environment == 'production' %} {% unless jekyll.environment == 'production' %}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css">
{% endunless %} {% endunless %}
<!-- Theme style --> <!-- Theme style -->
@ -97,11 +97,32 @@
<link rel="stylesheet" href="{{ site.data.origin[type].glightbox.css | relative_url }}"> <link rel="stylesheet" href="{{ site.data.origin[type].glightbox.css | relative_url }}">
{% endif %} {% endif %}
<!-- JavaScript --> <!-- Scripts -->
{% unless site.theme_mode %} {% unless site.theme_mode %}
{% include mode-toggle.html %} <script src="{{ '/assets/js/dist/theme.min.js' | relative_url }}"></script>
{% endunless %} {% endunless %}
{% include js-selector.html lang=lang %}
{% if jekyll.environment == 'production' %}
<!-- PWA -->
{% if site.pwa.enabled %}
<script
defer
src="{{ '/app.min.js' | relative_url }}?baseurl={{ site.baseurl | default: '' }}&register={{ site.pwa.cache.enabled }}"
></script>
{% endif %}
<!-- Web Analytics -->
{% for analytics in site.analytics %}
{% capture str %}{{ analytics }}{% endcapture %}
{% assign platform = str | split: '{' | first %}
{% if site.analytics[platform].id and site.analytics[platform].id != empty %}
{% include analytics/{{ platform }}.html %}
{% endif %}
{% endfor %}
{% endif %}
{% include metadata-hook.html %} {% include metadata-hook.html %}
</head> </head>

View file

@ -62,12 +62,12 @@
{% capture script %}/assets/js/dist/{{ js }}.min.js{% endcapture %} {% capture script %}/assets/js/dist/{{ js }}.min.js{% endcapture %}
<script src="{{ script | relative_url }}"></script> <script defer src="{{ script | relative_url }}"></script>
{% if page.math %} {% if page.math %}
<!-- MathJax --> <!-- MathJax -->
<script src="{{ '/assets/js/data/mathjax.js' | relative_url }}"></script> <script async src="{{ '/assets/js/data/mathjax.js' | relative_url }}"></script>
<script src="https://cdnjs.cloudflare.com/polyfill/v3/polyfill.min.js?features=es6"></script> <script async src="https://cdnjs.cloudflare.com/polyfill/v3/polyfill.min.js?features=es6"></script>
<script id="MathJax-script" async src="{{ site.data.origin[type].mathjax.js | relative_url }}"></script> <script id="MathJax-script" async src="{{ site.data.origin[type].mathjax.js | relative_url }}"></script>
{% endif %} {% endif %}
@ -84,26 +84,3 @@
{% endcase %} {% endcase %}
{% endif %} {% endif %}
{% endif %} {% endif %}
{% if page.mermaid %}
{% include mermaid.html %}
{% endif %}
{% if jekyll.environment == 'production' %}
<!-- PWA -->
{% if site.pwa.enabled %}
<script
defer
src="{{ 'app.min.js' | relative_url }}?baseurl={{ site.baseurl | default: '' }}&register={{ site.pwa.cache.enabled }}"
></script>
{% endif %}
<!-- Web Analytics -->
{% for analytics in site.analytics %}
{% capture str %}{{ analytics }}{% endcapture %}
{% assign type = str | split: '{' | first %}
{% if site.analytics[type].id and site.analytics[type].id != empty %}
{% include analytics/{{ type }}.html %}
{% endif %}
{% endfor %}
{% endif %}

View file

@ -1,6 +1,6 @@
{% assign urls = include.urls | split: ',' %} {% assign urls = include.urls | split: ',' %}
{% assign combined_urls = nil %} {% assign combined_urls = null %}
{% assign domain = 'https://cdn.jsdelivr.net/' %} {% assign domain = 'https://cdn.jsdelivr.net/' %}
@ -15,12 +15,12 @@
{% endif %} {% endif %}
{% elsif url contains '//' %} {% elsif url contains '//' %}
<script src="{{ url }}"></script> <script defer src="{{ url }}"></script>
{% else %} {% else %}
<script src="{{ url | relative_url }}"></script> <script defer src="{{ url | relative_url }}"></script>
{% endif %} {% endif %}
{% endfor %} {% endfor %}
{% if combined_urls %} {% if combined_urls %}
<script src="{{ combined_urls }}"></script> <script defer src="{{ combined_urls }}"></script>
{% endif %} {% endif %}

View file

@ -1,62 +0,0 @@
<!-- mermaid-js loader -->
<script type="text/javascript">
function updateMermaid(event) {
if (event.source === window && event.data && event.data.direction === ModeToggle.ID) {
const mode = event.data.message;
if (typeof mermaid === 'undefined') {
return;
}
let expectedTheme = mode === ModeToggle.DARK_MODE ? 'dark' : 'default';
let config = { theme: expectedTheme };
{%- comment -%}
Re-render the SVG <https://github.com/mermaid-js/mermaid/issues/311#issuecomment-332557344>
{%- endcomment -%}
const mermaidList = document.getElementsByClassName('mermaid');
[...mermaidList].forEach((elem) => {
const svgCode = elem.previousSibling.children.item(0).innerHTML;
elem.innerHTML = svgCode;
elem.removeAttribute('data-processed');
});
mermaid.initialize(config);
mermaid.init(undefined, '.mermaid');
}
}
(function () {
let initTheme = 'default';
const html = document.documentElement;
if (
(html.hasAttribute('data-mode') && html.getAttribute('data-mode') === 'dark') ||
(!html.hasAttribute('data-mode') && window.matchMedia('(prefers-color-scheme: dark)').matches)
) {
initTheme = 'dark';
}
let mermaidConf = {
theme: initTheme {%- comment -%} <default | dark | forest | neutral> {%- endcomment -%}
};
{%- comment -%} Create mermaid tag {%- endcomment -%}
const basicList = document.getElementsByClassName('language-mermaid');
[...basicList].forEach((elem) => {
const svgCode = elem.textContent;
const backup = elem.parentElement;
backup.classList.add('d-none');
{%- comment -%} create mermaid node {%- endcomment -%}
let mermaid = document.createElement('pre');
mermaid.classList.add('mermaid');
const text = document.createTextNode(svgCode);
mermaid.appendChild(text);
backup.after(mermaid);
});
mermaid.initialize(mermaidConf);
window.addEventListener('message', updateMermaid);
})();
</script>

View file

@ -1,116 +0,0 @@
<!-- Switch the mode between dark and light. -->
<script type="text/javascript">
class ModeToggle {
static get MODE_KEY() {
return 'mode';
}
static get MODE_ATTR() {
return 'data-mode';
}
static get DARK_MODE() {
return 'dark';
}
static get LIGHT_MODE() {
return 'light';
}
static get ID() {
return 'mode-toggle';
}
constructor() {
let self = this;
{%- comment -%} always follow the system prefers {%- endcomment -%}
this.sysDarkPrefers.addEventListener('change', () => {
if (self.hasMode) {
self.clearMode();
}
self.notify();
});
if (!this.hasMode) {
return;
}
if (this.isDarkMode) {
this.setDark();
} else {
this.setLight();
}
}
get sysDarkPrefers() {
return window.matchMedia('(prefers-color-scheme: dark)');
}
get isPreferDark() {
return this.sysDarkPrefers.matches;
}
get isDarkMode() {
return this.mode === ModeToggle.DARK_MODE;
}
get hasMode() {
return this.mode != null;
}
get mode() {
return sessionStorage.getItem(ModeToggle.MODE_KEY);
}
{%- comment -%} get the current mode on screen {%- endcomment -%}
get modeStatus() {
if (this.hasMode) {
return this.mode;
} else {
return this.isPreferDark ? ModeToggle.DARK_MODE : ModeToggle.LIGHT_MODE;
}
}
setDark() {
document.documentElement.setAttribute(ModeToggle.MODE_ATTR, ModeToggle.DARK_MODE);
sessionStorage.setItem(ModeToggle.MODE_KEY, ModeToggle.DARK_MODE);
}
setLight() {
document.documentElement.setAttribute(ModeToggle.MODE_ATTR, ModeToggle.LIGHT_MODE);
sessionStorage.setItem(ModeToggle.MODE_KEY, ModeToggle.LIGHT_MODE);
}
clearMode() {
document.documentElement.removeAttribute(ModeToggle.MODE_ATTR);
sessionStorage.removeItem(ModeToggle.MODE_KEY);
}
{%- comment -%}
Notify another plugins that the theme mode has changed
{%- endcomment -%}
notify() {
window.postMessage(
{
direction: ModeToggle.ID,
message: this.modeStatus
},
'*'
);
}
flipMode() {
if (this.hasMode) {
this.clearMode();
} else {
if (this.isPreferDark) {
this.setLight();
} else {
this.setDark();
}
}
this.notify();
}
}
const modeToggle = new ModeToggle();
</script>

View file

@ -19,29 +19,31 @@
{% capture not_found %}<p class="mt-5">{{ site.data.locales[include.lang].search.no_results }}</p>{% endcapture %} {% capture not_found %}<p class="mt-5">{{ site.data.locales[include.lang].search.no_results }}</p>{% endcapture %}
<script> <script>
{%- comment -%} Note: dependent library will be loaded in `js-selector.html` {%- endcomment -%} {% comment %} Note: dependent library will be loaded in `js-selector.html` {% endcomment %}
SimpleJekyllSearch({ document.addEventListener('DOMContentLoaded', () => {
searchInput: document.getElementById('search-input'), SimpleJekyllSearch({
resultsContainer: document.getElementById('search-results'), searchInput: document.getElementById('search-input'),
json: '{{ '/assets/js/data/search.json' | relative_url }}', resultsContainer: document.getElementById('search-results'),
searchResultTemplate: '{{ result_elem | strip_newlines }}', json: '{{ '/assets/js/data/search.json' | relative_url }}',
noResultsText: '{{ not_found }}', searchResultTemplate: '{{ result_elem | strip_newlines }}',
templateMiddleware: function(prop, value, template) { noResultsText: '{{ not_found }}',
if (prop === 'categories') { templateMiddleware: function(prop, value, template) {
if (value === '') { if (prop === 'categories') {
return `${value}`; if (value === '') {
} else { return `${value}`;
return `<div class="me-sm-4"><i class="far fa-folder fa-fw"></i>${value}</div>`; } else {
return `<div class="me-sm-4"><i class="far fa-folder fa-fw"></i>${value}</div>`;
}
} }
}
if (prop === 'tags') { if (prop === 'tags') {
if (value === '') { if (value === '') {
return `${value}`; return `${value}`;
} else { } else {
return `<div><i class="fa fa-tag fa-fw"></i>${value}</div>`; return `<div><i class="fa fa-tag fa-fw"></i>${value}</div>`;
}
} }
} }
} });
}); });
</script> </script>

View file

@ -11,9 +11,7 @@
{%- endif -%} {%- endif -%}
</a> </a>
<h1 class="site-title"> <a class="site-title d-block" href="{{ '/' | relative_url }}">{{ site.title }}</a>
<a href="{{ '/' | relative_url }}">{{ site.title }}</a>
</h1>
<p class="site-subtitle fst-italic mb-0">{{ site.tagline }}</p> <p class="site-subtitle fst-italic mb-0">{{ site.tagline }}</p>
</header> </header>
<!-- .profile-wrapper --> <!-- .profile-wrapper -->

10
_includes/toc-status.html Normal file
View file

@ -0,0 +1,10 @@
{% comment %}
Determine TOC state and return it through variable "enable_toc"
{% endcomment %}
{% assign enable_toc = false %}
{% if site.toc and page.toc %}
{% if page.content contains '<h2' or page.content contains '<h3' %}
{% assign enable_toc = true %}
{% endif %}
{% endif %}

View file

@ -1,13 +1,9 @@
{% assign enable_toc = false %} {% include toc-status.html %}
{% if site.toc and page.toc %}
{% if page.content contains '<h2' or page.content contains '<h3' %}
{% assign enable_toc = true %}
{% endif %}
{% endif %}
{% if enable_toc %} {% if enable_toc %}
<section id="toc-wrapper" class="d-none ps-0 pe-4"> <div class="toc-border-cover z-3"></div>
<h2 class="panel-heading ps-3 mb-2">{{- site.data.locales[include.lang].panel.toc -}}</h2> <section id="toc-wrapper" class="position-sticky ps-0 pe-4">
<h2 class="panel-heading ps-3 pb-2 mb-0">{{- site.data.locales[include.lang].panel.toc -}}</h2>
<nav id="toc"></nav> <nav id="toc"></nav>
</section> </section>
{% endif %} {% endif %}

View file

@ -1,5 +1,5 @@
import { basic, initSidebar, initTopbar } from './modules/layouts'; import { basic, initSidebar, initTopbar } from './modules/layouts';
import { categoryCollapse } from './modules/plugins'; import { categoryCollapse } from './modules/components';
basic(); basic();
initSidebar(); initSidebar();

View file

@ -1,5 +1,5 @@
import { basic, initSidebar, initTopbar } from './modules/layouts'; import { basic, initSidebar, initTopbar } from './modules/layouts';
import { initLocaleDatetime, loadImg } from './modules/plugins'; import { initLocaleDatetime, loadImg } from './modules/components';
loadImg(); loadImg();
initLocaleDatetime(); initLocaleDatetime();

View file

@ -1,5 +1,5 @@
import { basic, initSidebar, initTopbar } from './modules/layouts'; import { basic, initSidebar, initTopbar } from './modules/layouts';
import { initLocaleDatetime } from './modules/plugins'; import { initLocaleDatetime } from './modules/components';
initSidebar(); initSidebar();
initTopbar(); initTopbar();

View file

@ -3,4 +3,8 @@ export { initClipboard } from './components/clipboard';
export { loadImg } from './components/img-loading'; export { loadImg } from './components/img-loading';
export { imgPopup } from './components/img-popup'; export { imgPopup } from './components/img-popup';
export { initLocaleDatetime } from './components/locale-datetime'; export { initLocaleDatetime } from './components/locale-datetime';
export { toc } from './components/toc'; export { initToc } from './components/toc';
export { loadMermaid } from './components/mermaid';
export { modeWatcher } from './components/mode-toggle';
export { back2top } from './components/back-to-top';
export { loadTooptip } from './components/tooltip-loader';

View file

@ -4,7 +4,6 @@
* Dependencies: https://github.com/biati-digital/glightbox * Dependencies: https://github.com/biati-digital/glightbox
*/ */
const html = document.documentElement;
const lightImages = '.popup:not(.dark)'; const lightImages = '.popup:not(.dark)';
const darkImages = '.popup:not(.light)'; const darkImages = '.popup:not(.light)';
let selector = lightImages; let selector = lightImages;
@ -33,26 +32,17 @@ export function imgPopup() {
document.querySelector('.popup.dark') === null document.querySelector('.popup.dark') === null
); );
if ( if (Theme.visualState === Theme.DARK) {
(html.hasAttribute('data-mode') &&
html.getAttribute('data-mode') === 'dark') ||
(!html.hasAttribute('data-mode') &&
window.matchMedia('(prefers-color-scheme: dark)').matches)
) {
selector = darkImages; selector = darkImages;
} }
let current = GLightbox({ selector: `${selector}` }); let current = GLightbox({ selector: `${selector}` });
if (hasDualImages && document.getElementById('mode-toggle')) { if (hasDualImages && Theme.switchable) {
let reverse = null; let reverse = null;
window.addEventListener('message', (event) => { window.addEventListener('message', (event) => {
if ( if (event.source === window && event.data && event.data.id === Theme.ID) {
event.source === window &&
event.data &&
event.data.direction === ModeToggle.ID
) {
updateImages(current, reverse); updateImages(current, reverse);
} }
}); });

View file

@ -0,0 +1,60 @@
/**
* Mermaid-js loader
*/
const MERMAID = 'mermaid';
const themeMapper = Theme.getThemeMapper('default', 'dark');
function refreshTheme(event) {
if (event.source === window && event.data && event.data.id === Theme.ID) {
// Re-render the SVG <https://github.com/mermaid-js/mermaid/issues/311#issuecomment-332557344>
const mermaidList = document.getElementsByClassName(MERMAID);
[...mermaidList].forEach((elem) => {
const svgCode = elem.previousSibling.children.item(0).innerHTML;
elem.textContent = svgCode;
elem.removeAttribute('data-processed');
});
const newTheme = themeMapper[Theme.visualState];
mermaid.initialize({ theme: newTheme });
mermaid.init(null, `.${MERMAID}`);
}
}
function setNode(elem) {
const svgCode = elem.textContent;
const backup = elem.parentElement;
backup.classList.add('d-none');
// Create mermaid node
const mermaid = document.createElement('pre');
mermaid.classList.add(MERMAID);
const text = document.createTextNode(svgCode);
mermaid.appendChild(text);
backup.after(mermaid);
}
export function loadMermaid() {
if (
typeof mermaid === 'undefined' ||
typeof mermaid.initialize !== 'function'
) {
return;
}
const initTheme = themeMapper[Theme.visualState];
let mermaidConf = {
theme: initTheme
};
const basicList = document.getElementsByClassName('language-mermaid');
[...basicList].forEach(setNode);
mermaid.initialize(mermaidConf);
if (Theme.switchable) {
window.addEventListener('message', refreshTheme);
}
}

View file

@ -0,0 +1,15 @@
/**
* Add listener for theme mode toggle
*/
const $toggle = document.getElementById('mode-toggle');
export function modeWatcher() {
if (!$toggle) {
return;
}
$toggle.addEventListener('click', () => {
Theme.flip();
});
}

View file

@ -1,14 +0,0 @@
/**
* Add listener for theme mode toggle
*/
const toggle = document.getElementById('mode-toggle');
export function modeWatcher() {
if (!toggle) {
return;
}
toggle.addEventListener('click', () => {
modeToggle.flipMode();
});
}

View file

@ -1,27 +0,0 @@
/**
* Expand or close the sidebar in mobile screens.
*/
const ATTR_DISPLAY = 'sidebar-display';
class SidebarUtil {
static isExpanded = false;
static toggle() {
if (SidebarUtil.isExpanded === false) {
document.body.setAttribute(ATTR_DISPLAY, '');
} else {
document.body.removeAttribute(ATTR_DISPLAY);
}
SidebarUtil.isExpanded = !SidebarUtil.isExpanded;
}
}
export function sidebarExpand() {
document
.getElementById('sidebar-trigger')
.addEventListener('click', SidebarUtil.toggle);
document.getElementById('mask').addEventListener('click', SidebarUtil.toggle);
}

View file

@ -1,15 +1,33 @@
export function toc() { import { TocMobile as mobile } from './toc/toc-mobile';
if (document.querySelector('main h2, main h3')) { import { TocDesktop as desktop } from './toc/toc-desktop';
// see: https://github.com/tscanlin/tocbot#usage
tocbot.init({
tocSelector: '#toc',
contentSelector: '.content',
ignoreSelector: '[data-toc-skip]',
headingSelector: 'h2, h3, h4',
orderedList: false,
scrollSmooth: false
});
document.getElementById('toc-wrapper').classList.remove('d-none'); const desktopMode = matchMedia('(min-width: 1200px)');
function refresh(e) {
if (e.matches) {
if (mobile.popupOpened) {
mobile.hidePopup();
}
desktop.refresh();
} else {
mobile.refresh();
} }
} }
function init() {
if (document.querySelector('main>article[data-toc="true"]') === null) {
return;
}
// Avoid create multiple instances of Tocbot. Ref: <https://github.com/tscanlin/tocbot/issues/203>
if (desktopMode.matches) {
desktop.init();
} else {
mobile.init();
}
desktopMode.onchange = refresh;
}
export { init as initToc };

View file

@ -0,0 +1,22 @@
export class TocDesktop {
/* Tocbot options Ref: https://github.com/tscanlin/tocbot#usage */
static options = {
tocSelector: '#toc',
contentSelector: '.content',
ignoreSelector: '[data-toc-skip]',
headingSelector: 'h2, h3, h4',
orderedList: false,
scrollSmooth: false,
headingsOffset: 16 * 2 // 2rem
};
static refresh() {
tocbot.refresh(this.options);
}
static init() {
if (document.getElementById('toc-wrapper')) {
tocbot.init(this.options);
}
}
}

View file

@ -0,0 +1,125 @@
/**
* TOC button, topbar and popup for mobile devices
*/
const $tocBar = document.getElementById('toc-bar');
const $soloTrigger = document.getElementById('toc-solo-trigger');
const $triggers = document.getElementsByClassName('toc-trigger');
const $popup = document.getElementById('toc-popup');
const $btnClose = document.getElementById('toc-popup-close');
const SCROLL_LOCK = 'overflow-hidden';
const CLOSING = 'closing';
export class TocMobile {
static #invisible = true;
static #barHeight = 16 * 3; // 3rem
static options = {
tocSelector: '#toc-popup-content',
contentSelector: '.content',
ignoreSelector: '[data-toc-skip]',
headingSelector: 'h2, h3, h4',
orderedList: false,
scrollSmooth: false,
collapseDepth: 4,
headingsOffset: this.#barHeight
};
static initBar() {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
$tocBar.classList.toggle('invisible', entry.isIntersecting);
});
},
{ rootMargin: `-${this.#barHeight}px 0px 0px 0px` }
);
observer.observe($soloTrigger);
this.#invisible = false;
}
static listenAnchors() {
const $anchors = document.getElementsByClassName('toc-link');
[...$anchors].forEach((anchor) => {
anchor.onclick = () => this.hidePopup();
});
}
static refresh() {
if (this.#invisible) {
this.initComponents();
}
tocbot.refresh(this.options);
this.listenAnchors();
}
static get popupOpened() {
return $popup.open;
}
static showPopup() {
this.lockScroll(true);
$popup.showModal();
const activeItem = $popup.querySelector('li.is-active-li');
activeItem.scrollIntoView({ block: 'center' });
}
static hidePopup() {
$popup.toggleAttribute(CLOSING);
$popup.addEventListener(
'animationend',
() => {
$popup.toggleAttribute(CLOSING);
$popup.close();
},
{ once: true }
);
this.lockScroll(false);
}
static lockScroll(enable) {
document.documentElement.classList.toggle(SCROLL_LOCK, enable);
document.body.classList.toggle(SCROLL_LOCK, enable);
}
static clickBackdrop(event) {
if ($popup.hasAttribute(CLOSING)) {
return;
}
const rect = event.target.getBoundingClientRect();
if (
event.clientX < rect.left ||
event.clientX > rect.right ||
event.clientY < rect.top ||
event.clientY > rect.bottom
) {
this.hidePopup();
}
}
static initComponents() {
this.initBar();
[...$triggers].forEach((trigger) => {
trigger.onclick = () => this.showPopup();
});
$popup.onclick = (e) => this.clickBackdrop(e);
$btnClose.onclick = () => this.hidePopup();
$popup.oncancel = (e) => {
e.preventDefault();
this.hidePopup();
};
}
static init() {
tocbot.init(this.options);
this.listenAnchors();
this.initComponents();
}
}

View file

@ -1,7 +1,7 @@
import { back2top } from '../components/back-to-top'; import { back2top, loadTooptip, modeWatcher } from '../components';
import { loadTooptip } from '../components/tooltip-loader';
export function basic() { export function basic() {
modeWatcher();
back2top(); back2top();
loadTooptip(); loadTooptip();
} }

View file

@ -1,7 +1,19 @@
import { modeWatcher } from '../components/mode-watcher'; const ATTR_DISPLAY = 'sidebar-display';
import { sidebarExpand } from '../components/sidebar'; const $sidebar = document.getElementById('sidebar');
const $trigger = document.getElementById('sidebar-trigger');
const $mask = document.getElementById('mask');
class SidebarUtil {
static #isExpanded = false;
static toggle() {
this.#isExpanded = !this.#isExpanded;
document.body.toggleAttribute(ATTR_DISPLAY, this.#isExpanded);
$sidebar.classList.toggle('z-2', this.#isExpanded);
$mask.classList.toggle('d-none', !this.#isExpanded);
}
}
export function initSidebar() { export function initSidebar() {
modeWatcher(); $trigger.onclick = $mask.onclick = () => SidebarUtil.toggle();
sidebarExpand();
} }

View file

@ -0,0 +1,135 @@
/**
* Theme management class
*
* To reduce flickering during page load, this script should be loaded synchronously.
*/
class Theme {
static #modeKey = 'mode';
static #modeAttr = 'data-mode';
static #darkMedia = window.matchMedia('(prefers-color-scheme: dark)');
static switchable = !document.documentElement.hasAttribute(this.#modeAttr);
static get DARK() {
return 'dark';
}
static get LIGHT() {
return 'light';
}
/**
* @returns {string} Theme mode identifier
*/
static get ID() {
return 'theme-mode';
}
/**
* Gets the current visual state of the theme.
*
* @returns {string} The current visual state, either the mode if it exists,
* or the system dark mode state ('dark' or 'light').
*/
static get visualState() {
if (this.#hasMode) {
return this.#mode;
} else {
return this.#sysDark ? this.DARK : this.LIGHT;
}
}
static get #mode() {
return sessionStorage.getItem(this.#modeKey);
}
static get #isDarkMode() {
return this.#mode === this.DARK;
}
static get #hasMode() {
return this.#mode !== null;
}
static get #sysDark() {
return this.#darkMedia.matches;
}
/**
* Maps theme modes to provided values
* @param {string} light Value for light mode
* @param {string} dark Value for dark mode
* @returns {Object} Mapped values
*/
static getThemeMapper(light, dark) {
return {
[this.LIGHT]: light,
[this.DARK]: dark
};
}
/**
* Initializes the theme based on system preferences or stored mode
*/
static init() {
if (!this.switchable) {
return;
}
this.#darkMedia.addEventListener('change', () => {
const lastMode = this.#mode;
this.#clearMode();
if (lastMode !== this.visualState) {
this.#notify();
}
});
if (!this.#hasMode) {
return;
}
if (this.#isDarkMode) {
this.#setDark();
} else {
this.#setLight();
}
}
/**
* Flips the current theme mode
*/
static flip() {
if (this.#hasMode) {
this.#clearMode();
} else {
this.#sysDark ? this.#setLight() : this.#setDark();
}
this.#notify();
}
static #setDark() {
document.documentElement.setAttribute(this.#modeAttr, this.DARK);
sessionStorage.setItem(this.#modeKey, this.DARK);
}
static #setLight() {
document.documentElement.setAttribute(this.#modeAttr, this.LIGHT);
sessionStorage.setItem(this.#modeKey, this.LIGHT);
}
static #clearMode() {
document.documentElement.removeAttribute(this.#modeAttr);
sessionStorage.removeItem(this.#modeKey);
}
/**
* Notifies other plugins that the theme mode has changed
*/
static #notify() {
window.postMessage({ id: this.ID }, '*');
}
}
Theme.init();
export default Theme;

View file

@ -1,9 +1,15 @@
import { basic, initSidebar, initTopbar } from './modules/layouts'; import { basic, initSidebar, initTopbar } from './modules/layouts';
import { loadImg, imgPopup, initClipboard } from './modules/plugins'; import {
loadImg,
imgPopup,
initClipboard,
loadMermaid
} from './modules/components';
loadImg(); loadImg();
imgPopup(); imgPopup();
initSidebar(); initSidebar();
initTopbar(); initTopbar();
initClipboard(); initClipboard();
loadMermaid();
basic(); basic();

View file

@ -1,17 +1,20 @@
import { basic, initSidebar, initTopbar } from './modules/layouts'; import { basic, initTopbar, initSidebar } from './modules/layouts';
import { import {
loadImg, loadImg,
imgPopup, imgPopup,
initLocaleDatetime, initLocaleDatetime,
initClipboard, initClipboard,
toc initToc,
} from './modules/plugins'; loadMermaid
} from './modules/components';
loadImg(); loadImg();
toc(); initToc();
imgPopup(); imgPopup();
initSidebar(); initSidebar();
initLocaleDatetime(); initLocaleDatetime();
initClipboard(); initClipboard();
initTopbar(); initTopbar();
loadMermaid();
basic(); basic();

View file

@ -33,7 +33,7 @@ layout: compress
</main> </main>
<!-- panel --> <!-- panel -->
<aside aria-label="Panel" id="panel-wrapper" class="col-xl-3 ps-2 mb-5 text-muted"> <aside aria-label="Panel" id="panel-wrapper" class="col-xl-3 ps-2 text-muted">
<div class="access"> <div class="access">
{% include_cached update-list.html lang=lang %} {% include_cached update-list.html lang=lang %}
{% include_cached trending-tags.html lang=lang %} {% include_cached trending-tags.html lang=lang %}
@ -68,14 +68,18 @@ layout: compress
</aside> </aside>
</div> </div>
<div id="mask"></div> <div id="mask" class="d-none position-fixed w-100 h-100 z-1"></div>
{% if site.pwa.enabled %} {% if site.pwa.enabled %}
{% include_cached notification.html lang=lang %} {% include_cached notification.html lang=lang %}
{% endif %} {% endif %}
<!-- JavaScripts --> <!-- Embedded scripts -->
{% include js-selector.html lang=lang %}
{% for _include in layout.script_includes %}
{% assign _include_path = _include | append: '.html' %}
{% include {{ _include_path }} %}
{% endfor %}
{% include_cached search-loader.html lang=lang %} {% include_cached search-loader.html lang=lang %}
</body> </body>

View file

@ -5,38 +5,45 @@ refactor: true
{% include lang.html %} {% include lang.html %}
{% assign pinned = site.posts | where: 'pin', 'true' %} {% assign all_pinned = site.posts | where: 'pin', 'true' %}
{% assign default = site.posts | where_exp: 'item', 'item.pin != true and item.hidden != true' %} {% assign all_normal = site.posts | where_exp: 'item', 'item.pin != true and item.hidden != true' %}
{% assign posts = '' | split: '' %} {% assign posts = '' | split: '' %}
<!-- Get pinned posts --> <!-- Get pinned posts on current page -->
{% assign offset = paginator.page | minus: 1 | times: paginator.per_page %} {% assign visible_start = paginator.page | minus: 1 | times: paginator.per_page %}
{% assign pinned_num = pinned.size | minus: offset %} {% assign visible_end = visible_start | plus: paginator.per_page %}
{% if pinned_num > 0 %} {% if all_pinned.size > visible_start %}
{% for i in (offset..pinned.size) limit: pinned_num %} {% if all_pinned.size > visible_end %}
{% assign posts = posts | push: pinned[i] %} {% assign pinned_size = paginator.per_page %}
{% else %}
{% assign pinned_size = all_pinned.size | minus: visible_start %}
{% endif %}
{% for i in (visible_start..all_pinned.size) limit: pinned_size %}
{% assign posts = posts | push: all_pinned[i] %}
{% endfor %} {% endfor %}
{% else %} {% else %}
{% assign pinned_num = 0 %} {% assign pinned_size = 0 %}
{% endif %} {% endif %}
<!-- Get default posts --> <!-- Get normal posts on current page -->
{% assign default_beg = offset | minus: pinned.size %} {% assign normal_size = paginator.posts | size | minus: pinned_size %}
{% if default_beg < 0 %} {% if normal_size > 0 %}
{% assign default_beg = 0 %} {% if pinned_size > 0 %}
{% endif %} {% assign normal_start = 0 %}
{% else %}
{% assign normal_start = visible_start | minus: all_pinned.size %}
{% endif %}
{% assign default_num = paginator.posts | size | minus: pinned_num %} {% assign normal_end = normal_start | plus: normal_size | minus: 1 %}
{% assign default_end = default_beg | plus: default_num | minus: 1 %}
{% if default_num > 0 %} {% for i in (normal_start..normal_end) %}
{% for i in (default_beg..default_end) %} {% assign posts = posts | push: all_normal[i] %}
{% assign posts = posts | push: default[i] %}
{% endfor %} {% endfor %}
{% endif %} {% endif %}

View file

@ -6,12 +6,15 @@ panel_includes:
tail_includes: tail_includes:
- related-posts - related-posts
- post-nav - post-nav
- comments script_includes:
- comment
--- ---
{% include lang.html %} {% include lang.html %}
<article class="px-1"> {% include toc-status.html %}
<article class="px-1" data-toc="{{ enable_toc }}">
<header> <header>
<h1 data-toc-skip>{{ page.title }}</h1> <h1 data-toc-skip>{{ page.title }}</h1>
{% if page.description %} {% if page.description %}
@ -95,6 +98,30 @@ tail_includes:
</div> </div>
</header> </header>
{% if enable_toc %}
<div id="toc-bar" class="d-flex align-items-center justify-content-between invisible">
<span class="label text-truncate">{{ page.title }}</span>
<button type="button" class="toc-trigger btn me-1">
<i class="fa-solid fa-list-ul fa-fw"></i>
</button>
</div>
<button id="toc-solo-trigger" type="button" class="toc-trigger btn btn-outline-secondary btn-sm">
<span class="label ps-2 pe-1">{{- site.data.locales[lang].panel.toc -}}</span>
<i class="fa-solid fa-angle-right fa-fw"></i>
</button>
<dialog id="toc-popup" class="p-0">
<div class="header d-flex flex-row align-items-center justify-content-between">
<div class="label text-truncate py-2 ms-4">{{- page.title -}}</div>
<button id="toc-popup-close" type="button" class="btn mx-1 my-1 opacity-75">
<i class="fas fa-close"></i>
</button>
</div>
<div id="toc-popup-content" class="px-4 py-3 pb-4"></div>
</dialog>
{% endif %}
<div class="content"> <div class="content">
{{ content }} {{ content }}
</div> </div>

View file

@ -93,8 +93,6 @@ Social contact options are displayed at the bottom of the sidebar. You can enabl
To customize the stylesheet, copy the theme's `assets/css/jekyll-theme-chirpy.scss`{: .filepath} file to the same path in your Jekyll site, and add your custom styles at the end of the file. To customize the stylesheet, copy the theme's `assets/css/jekyll-theme-chirpy.scss`{: .filepath} file to the same path in your Jekyll site, and add your custom styles at the end of the file.
Starting with version `6.2.0`, if you want to overwrite the SASS variables defined in `_sass/addon/variables.scss`{: .filepath}, copy the main SASS file `_sass/main.scss`{: .filepath} to the `_sass`{: .filepath} directory in your site's source, then create a new file `_sass/variables-hook.scss`{: .filepath} and assign your new values there.
### Customizing Static Assets ### Customizing Static Assets
Static assets configuration was introduced in version `5.1.0`. The CDN of the static assets is defined in `_data/origin/cors.yml`{: .filepath }. You can replace some of them based on the network conditions in the region where your website is published. Static assets configuration was introduced in version `5.1.0`. The CDN of the static assets is defined in `_data/origin/cors.yml`{: .filepath }. You can replace some of them based on the network conditions in the region where your website is published.

View file

@ -0,0 +1,73 @@
@use 'sass:map';
$-breakpoints: (
// 1 column
sm: 576px,
md: 768px,
// 2 columns
lg: 850px,
// 3 columns
xl: 1200px,
xxl: 1400px,
xxxl: 1650px
);
@function get($breakpoint) {
@return map.get($-breakpoints, $breakpoint);
}
/* Less than the given width */
@mixin lt($width) {
@media all and (max-width: calc(#{$width} - 1px)) {
@content;
}
}
/* Less than or equal to the given width */
@mixin lte($width) {
@media all and (max-width: $width) {
@content;
}
}
@mixin sm {
@media all and (min-width: get(sm)) {
@content;
}
}
@mixin md {
@media all and (min-width: get(md)) {
@content;
}
}
@mixin lg {
@media all and (min-width: get(lg)) {
@content;
}
}
@mixin xl {
@media all and (min-width: get(xl)) {
@content;
}
}
@mixin xxl {
@media all and (min-width: get(xxl)) {
@content;
}
}
@mixin xxxl {
@media all and (min-width: get(xxxl)) {
@content;
}
}
@mixin between($min, $max) {
@media all and (min-width: $min) and (max-width: $max) {
@content;
}
}

View file

@ -0,0 +1,4 @@
@forward 'variables';
@forward 'mixins';
@forward 'placeholders';
@forward 'breakpoints';

View file

@ -0,0 +1,80 @@
@mixin text-ellipsis {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
@mixin mt-mb($value) {
margin-top: $value;
margin-bottom: $value;
}
@mixin ml-mr($value) {
margin-left: $value;
margin-right: $value;
}
@mixin pt-pb($val) {
padding-top: $val;
padding-bottom: $val;
}
@mixin pl-pr($val, $important: false) {
@if $important {
padding-left: $val !important;
padding-right: $val !important;
} @else {
padding-left: $val;
padding-right: $val;
}
}
@mixin placeholder {
color: var(--text-muted-color) !important;
}
@mixin placeholder-focus {
opacity: 0.6;
}
@mixin label($font-size: 1rem, $font-weight: 600, $color: var(--label-color)) {
color: $color;
font-size: $font-size;
font-weight: $font-weight;
}
@mixin align-center {
position: relative;
left: 50%;
transform: translateX(-50%);
}
@mixin prompt($type, $fa-content, $fa-style: 'solid', $rotate: 0) {
&.prompt-#{$type} {
background-color: var(--prompt-#{$type}-bg);
&::before {
content: $fa-content;
color: var(--prompt-#{$type}-icon-color);
font: var(--fa-font-#{$fa-style});
@if $rotate != 0 {
transform: rotate(#{$rotate}deg);
}
}
}
}
@mixin slide($append: null) {
$basic: transform 0.4s ease;
@if $append {
transition: $basic, $append;
} @else {
transition: $basic;
}
}
@mixin max-w-100 {
max-width: 100%;
}

View file

@ -1,13 +1,11 @@
/* @use 'variables' as v;
* Mainly scss modules, only imported to `assets/css/main.scss` @use 'mixins' as mx;
*/
/* ---------- scss placeholder --------- */
%heading { %heading {
color: var(--heading-color); color: var(--heading-color);
font-weight: 400; font-weight: 400;
font-family: $font-family-heading; font-family: v.$font-family-heading;
scroll-margin-top: 3.5rem;
} }
%anchor { %anchor {
@ -81,7 +79,7 @@
} }
%rounded { %rounded {
border-radius: $radius-lg; border-radius: v.$radius-lg;
} }
%img-caption { %img-caption {
@ -111,6 +109,10 @@
-webkit-box-orient: vertical; -webkit-box-orient: vertical;
} }
%text-ellipsis {
@include mx.text-ellipsis;
}
%text-highlight { %text-highlight {
color: var(--text-muted-highlight-color); color: var(--text-muted-highlight-color);
font-weight: 600; font-weight: 600;
@ -134,60 +136,25 @@
} }
} }
/* ---------- scss mixin --------- */ %btn-color {
button i {
@mixin mt-mb($value) { color: #999999;
margin-top: $value;
margin-bottom: $value;
}
@mixin ml-mr($value) {
margin-left: $value;
margin-right: $value;
}
@mixin pt-pb($val) {
padding-top: $val;
padding-bottom: $val;
}
@mixin pl-pr($val) {
padding-left: $val;
padding-right: $val;
}
@mixin placeholder {
color: var(--text-muted-color) !important;
}
@mixin placeholder-focus {
opacity: 0.6;
}
@mixin label($font-size: 1rem, $font-weight: 600, $color: var(--label-color)) {
color: $color;
font-size: $font-size;
font-weight: $font-weight;
}
@mixin align-center {
position: relative;
left: 50%;
transform: translateX(-50%);
}
@mixin prompt($type, $fa-content, $fa-style: 'solid', $rotate: 0) {
&.prompt-#{$type} {
background-color: var(--prompt-#{$type}-bg);
&::before {
content: $fa-content;
color: var(--prompt-#{$type}-icon-color);
font: var(--fa-font-#{$fa-style});
@if $rotate != 0 {
transform: rotate(#{$rotate}deg);
}
}
} }
} }
%code-snippet-bg {
background-color: var(--highlight-bg-color);
}
%code-snippet-padding {
padding-left: 1rem;
padding-right: 1.5rem;
}
%max-w-100 {
max-width: 100%;
}
%panel-border {
border-left: 1px solid var(--main-border-color);
}

View file

@ -1,7 +1,3 @@
/*
* The SCSS variables
*/
/* sidebar */ /* sidebar */
$sidebar-width: 260px !default; /* the basic width */ $sidebar-width: 260px !default; /* the basic width */

File diff suppressed because it is too large Load diff

476
_sass/base/_base.scss Normal file
View file

@ -0,0 +1,476 @@
@use '../abstracts/variables' as v;
@use '../abstracts/breakpoints' as bp;
@use '../abstracts/mixins' as mx;
@use '../abstracts/placeholders';
@use '../themes/light';
@use '../themes/dark';
:root {
font-size: 16px;
}
html {
@media (prefers-color-scheme: light) {
&:not([data-mode]),
&[data-mode='light'] {
@include light.styles;
}
&[data-mode='dark'] {
@include dark.styles;
}
}
@media (prefers-color-scheme: dark) {
&:not([data-mode]),
&[data-mode='dark'] {
@include dark.styles;
}
&[data-mode='light'] {
@include light.styles;
}
}
@include bp.lg {
overflow-y: scroll;
}
}
body {
background: var(--main-bg);
padding: env(safe-area-inset-top) env(safe-area-inset-right)
env(safe-area-inset-bottom) env(safe-area-inset-left);
color: var(--text-color);
-webkit-font-smoothing: antialiased;
font-family: v.$font-family-base;
}
h1.dynamic-title {
@include bp.lt(bp.get(lg)) {
display: none;
~ .content {
margin-top: 2.5rem;
}
}
}
main {
&.col-12 {
@include bp.xxxl {
padding-right: 4.5rem !important;
}
}
}
.preview-img {
aspect-ratio: 40 / 21;
width: 100%;
height: 100%;
overflow: hidden;
@extend %rounded;
&:not(.no-bg) {
background: var(--img-bg);
}
img {
height: 100%;
-o-object-fit: cover;
object-fit: cover;
@extend %rounded;
@at-root #post-list & {
width: 100%;
}
}
}
.post-preview {
@extend %rounded;
border: 0;
background: var(--card-bg);
box-shadow: var(--card-shadow);
&::before {
@extend %rounded;
content: '';
width: 100%;
height: 100%;
position: absolute;
background-color: var(--card-hovor-bg);
opacity: 0;
transition: opacity 0.35s ease-in-out;
}
&:hover {
&::before {
opacity: 0.3;
}
}
}
.post-meta {
@extend %text-sm;
a {
&:not([class]):hover {
@extend %link-hover;
}
}
em {
@extend %normal-font-style;
}
}
.content {
font-size: 1.08rem;
margin-top: 2rem;
overflow-wrap: break-word;
@include bp.xl {
font-size: 1.03rem;
}
a {
&.popup {
@extend %no-cursor;
@extend %img-caption;
@include mx.mt-mb(0.5rem);
cursor: zoom-in;
}
&:not(.img-link) {
@extend %link-underline;
&:hover {
@extend %link-hover;
}
}
}
ol,
ul {
&:not([class]),
&.task-list {
-webkit-padding-start: 1.75rem;
padding-inline-start: 1.75rem;
li {
margin: 0.25rem 0;
padding-left: 0.25rem;
}
ol,
ul {
-webkit-padding-start: 1.25rem;
padding-inline-start: 1.25rem;
margin: 0.5rem 0;
}
}
}
ul.task-list {
-webkit-padding-start: 1.25rem;
padding-inline-start: 1.25rem;
li {
list-style-type: none;
padding-left: 0;
/* checkbox icon */
> i {
width: 2rem;
margin-left: -1.25rem;
color: var(--checkbox-color);
&.checked {
color: var(--checkbox-checked-color);
}
}
ul {
-webkit-padding-start: 1.75rem;
padding-inline-start: 1.75rem;
}
}
input[type='checkbox'] {
margin: 0 0.5rem 0.2rem -1.3rem;
vertical-align: middle;
}
} /* ul */
dl > dd {
margin-left: 1rem;
}
::marker {
color: var(--text-muted-color);
}
.table-wrapper > table {
@include bp.lg {
min-width: 70%;
}
}
} /* .content */
.tag:hover {
@extend %tag-hover;
}
.post-tag {
display: inline-block;
min-width: 2rem;
text-align: center;
border-radius: 0.5rem;
border: 1px solid var(--btn-border-color);
padding: 0 0.4rem;
color: var(--text-muted-color);
line-height: 1.3rem;
&:not(:last-child) {
margin-right: 0.2rem;
}
}
.rounded-10 {
border-radius: 10px !important;
}
.img-link {
color: transparent;
display: inline-flex;
}
.shimmer {
overflow: hidden;
position: relative;
background: var(--img-bg);
&::before {
content: '';
position: absolute;
background: var(--shimmer-bg);
height: 100%;
width: 100%;
-webkit-animation: shimmer 1.3s infinite;
animation: shimmer 1.3s infinite;
}
@-webkit-keyframes shimmer {
0% {
transform: translateX(-100%);
}
100% {
transform: translateX(100%);
}
}
@keyframes shimmer {
0% {
transform: translateX(-100%);
}
100% {
transform: translateX(100%);
}
}
}
.embed-video {
width: 100%;
height: 100%;
margin-bottom: 1rem;
aspect-ratio: 16 / 9;
@extend %rounded;
&.twitch {
aspect-ratio: 310 / 189;
}
&.file {
display: block;
width: auto;
height: auto;
max-width: 100%;
max-height: 100%;
margin: auto;
margin-bottom: 0;
}
@extend %img-caption;
}
.embed-audio {
width: 100%;
display: block;
@extend %img-caption;
}
/* --- Effects classes --- */
.flex-grow-1 {
flex-grow: 1 !important;
}
.btn-box-shadow {
box-shadow: var(--card-shadow);
}
/* overwrite bootstrap muted */
.text-muted {
color: var(--text-muted-color) !important;
}
/* Overwrite bootstrap tooltip */
.tooltip-inner {
font-size: 0.7rem;
max-width: 220px;
text-align: left;
}
/* Overwrite bootstrap outline button */
.btn.btn-outline-primary {
&:not(.disabled):hover {
border-color: #007bff !important;
}
}
.disabled {
color: rgb(206, 196, 196);
pointer-events: auto;
cursor: not-allowed;
}
.hide-border-bottom {
border-bottom: none !important;
}
.input-focus {
box-shadow: none;
border-color: var(--input-focus-border-color) !important;
background: center !important;
transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out;
}
.left {
float: left;
margin: 0.75rem 1rem 1rem 0;
}
.right {
float: right;
margin: 0.75rem 0 1rem 1rem;
}
/* --- Overriding --- */
/* mermaid */
.mermaid {
text-align: center;
}
/* MathJax */
mjx-container {
overflow-y: hidden;
min-width: auto !important;
}
@media (hover: hover) {
#sidebar ul > li:last-child::after {
transition: top 0.5s ease;
}
.nav-link {
transition: background-color 0.3s ease-in-out;
}
.post-preview {
transition: background-color 0.35s ease-in-out;
}
}
#mask {
inset: 0 0 0 0;
}
#main-wrapper {
position: relative;
@include mx.pl-pr(0);
@include bp.lt(bp.get(lg)) {
@include mx.slide;
}
@include bp.lg {
margin-left: v.$sidebar-width;
}
@include bp.xxxl {
margin-left: v.$sidebar-width-large;
}
> .container {
min-height: 100vh;
@include bp.lte(bp.get(md)) {
@include mx.max-w-100;
@include mx.pl-pr(0);
}
@include bp.lt(bp.get(lg)) {
max-width: 100%;
}
/* Pad horizontal */
@include bp.between(992px, calc(#{bp.get(xl)} - 1px)) {
.col-lg-11 {
flex: 0 0 96%;
max-width: 96%;
}
}
@include bp.lt(bp.get(xl)) {
> .row {
justify-content: center !important;
}
}
@include bp.xxxl {
max-width: v.$main-content-max-width;
@include mx.pl-pr(1.75rem, true);
}
}
}
/* --- basic wrappers --- */
#topbar-wrapper.row,
#main-wrapper > .container > .row,
#search-result-wrapper > .row {
@include mx.ml-mr(0);
}
#tail-wrapper {
@include bp.xxxl {
padding-right: 4.5rem !important;
}
> :not(script) {
margin-top: 3rem;
}
}

4
_sass/base/_index.scss Normal file
View file

@ -0,0 +1,4 @@
@forward 'reset';
@forward 'base';
@forward 'typography';
@forward 'syntax';

41
_sass/base/_reset.scss Normal file
View file

@ -0,0 +1,41 @@
@use '../abstracts/mixins' as *;
::-webkit-input-placeholder {
@include placeholder;
}
::-moz-placeholder {
@include placeholder;
}
:-ms-input-placeholder {
@include placeholder;
}
::-ms-input-placeholder {
@include placeholder;
}
::placeholder {
@include placeholder;
}
:focus::-webkit-input-placeholder {
@include placeholder-focus;
}
:focus::-moz-placeholder {
@include placeholder-focus;
}
:focus:-ms-input-placeholder {
@include placeholder-focus;
}
:focus::-ms-input-placeholder {
@include placeholder-focus;
}
:focus::placeholder {
@include placeholder-focus;
}

View file

@ -1,44 +1,7 @@
/* @use '../abstracts/variables' as v;
* The syntax highlight. @use '../abstracts/breakpoints' as bp;
*/ @use '../abstracts/mixins' as mx;
@use '../abstracts/placeholders';
@import 'colors/syntax-light';
@import 'colors/syntax-dark';
html {
@media (prefers-color-scheme: light) {
&:not([data-mode]),
&[data-mode='light'] {
@include light-syntax;
}
&[data-mode='dark'] {
@include dark-syntax;
}
}
@media (prefers-color-scheme: dark) {
&:not([data-mode]),
&[data-mode='dark'] {
@include dark-syntax;
}
&[data-mode='light'] {
@include light-syntax;
}
}
}
/* -- code snippets -- */
%code-snippet-bg {
background-color: var(--highlight-bg-color);
}
%code-snippet-padding {
padding-left: 1rem;
padding-right: 1.5rem;
}
.highlighter-rouge { .highlighter-rouge {
color: var(--highlighter-rouge-color); color: var(--highlighter-rouge-color);
@ -59,7 +22,7 @@ html {
pre { pre {
margin-bottom: 0; margin-bottom: 0;
font-size: $code-font-size; font-size: v.$code-font-size;
line-height: 1.4rem; line-height: 1.4rem;
word-wrap: normal; /* Fixed Safari overflow-x */ word-wrap: normal; /* Fixed Safari overflow-x */
} }
@ -101,10 +64,10 @@ code {
color: var(--code-color); color: var(--code-color);
&.highlighter-rouge { &.highlighter-rouge {
font-size: $code-font-size; font-size: v.$code-font-size;
padding: 3px 5px; padding: 3px 5px;
word-break: break-word; word-break: break-word;
border-radius: $radius-sm; border-radius: v.$radius-sm;
background-color: var(--inline-code-bg); background-color: var(--inline-code-bg);
} }
@ -150,9 +113,42 @@ div[class^='language-'] {
box-shadow: var(--language-border-color) 0 0 0 1px; box-shadow: var(--language-border-color) 0 0 0 1px;
.content > & { .content > & {
@include ml-mr(-1rem); @include mx.ml-mr(-1rem);
border-radius: 0; border-radius: 0;
@include bp.sm {
@include mx.ml-mr(0);
border-radius: v.$radius-lg;
}
}
.code-header {
@include bp.sm {
@include mx.ml-mr(0);
$dot-margin: 1rem;
&::before {
content: '';
display: inline-block;
margin-left: $dot-margin;
width: v.$code-dot-size;
height: v.$code-dot-size;
border-radius: 50%;
background-color: var(--code-header-muted-color);
box-shadow: (v.$code-dot-size + v.$code-dot-gap) 0 0
var(--code-header-muted-color),
(v.$code-dot-size + v.$code-dot-gap) * 2 0 0
var(--code-header-muted-color);
}
span {
// center the text of label
margin-left: calc(($dot-margin + v.$code-dot-size) / 2 * -1);
}
}
} }
.highlight { .highlight {
@ -184,18 +180,18 @@ div {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
height: $code-header-height; height: v.$code-header-height;
margin-left: 0.75rem; margin-left: 0.75rem;
margin-right: 0.25rem; margin-right: 0.25rem;
/* the label block */ /* the label block */
span { span {
line-height: $code-header-height; line-height: v.$code-header-height;
/* label icon */ /* label icon */
i { i {
font-size: 1rem; font-size: 1rem;
width: $code-icon-width; width: v.$code-icon-width;
color: var(--code-header-icon-color); color: var(--code-header-icon-color);
&.small { &.small {
@ -223,8 +219,8 @@ div {
@extend %rounded; @extend %rounded;
border: 1px solid transparent; border: 1px solid transparent;
height: $code-header-height; height: v.$code-header-height;
width: $code-header-height; width: v.$code-header-height;
padding: 0; padding: 0;
background-color: inherit; background-color: inherit;
@ -255,38 +251,3 @@ div {
} }
} }
} }
@media all and (min-width: 576px) {
div[class^='language-'] {
.content > & {
@include ml-mr(0);
border-radius: $radius-lg;
}
.code-header {
@include ml-mr(0);
$dot-margin: 1rem;
&::before {
content: '';
display: inline-block;
margin-left: $dot-margin;
width: $code-dot-size;
height: $code-dot-size;
border-radius: 50%;
background-color: var(--code-header-muted-color);
box-shadow: ($code-dot-size + $code-dot-gap) 0 0
var(--code-header-muted-color),
($code-dot-size + $code-dot-gap) * 2 0 0
var(--code-header-muted-color);
}
span {
// center the text of label
margin-left: calc(($dot-margin + $code-dot-size) / 2 * -1);
}
}
}
}

266
_sass/base/_typography.scss Normal file
View file

@ -0,0 +1,266 @@
@use '../abstracts/variables' as v;
@use '../abstracts/breakpoints' as bp;
@use '../abstracts/mixins' as mx;
@use '../abstracts/placeholders';
@for $i from 1 through 5 {
h#{$i} {
@extend %heading;
@if $i > 1 {
@extend %anchor;
}
@if $i < 5 {
$size-factor: 0.25rem;
@if $i > 1 {
$size-factor: 0.18rem;
main & {
@if $i == 2 {
margin: 2.5rem 0 1.25rem;
} @else {
margin: 2rem 0 1rem;
}
}
}
& {
font-size: 1rem + (5 - $i) * $size-factor;
}
} @else {
font-size: 1.05rem;
}
}
}
a {
@extend %link-color;
text-decoration: none;
}
img {
max-width: 100%;
height: auto;
transition: all 0.35s ease-in-out;
.blur & {
$blur: 20px;
-webkit-filter: blur($blur);
filter: blur($blur);
}
}
blockquote {
border-left: 0.125rem solid var(--blockquote-border-color);
padding-left: 1rem;
color: var(--blockquote-text-color);
margin-top: 0.5rem;
> p:last-child {
margin-bottom: 0;
}
&[class^='prompt-'] {
border-left: 0;
position: relative;
padding: 1rem 1rem 1rem 3rem;
color: var(--prompt-text-color);
@extend %rounded;
&::before {
text-align: center;
width: 3rem;
position: absolute;
left: 0.25rem;
margin-top: 0.4rem;
text-rendering: auto;
-webkit-font-smoothing: antialiased;
}
}
@include mx.prompt('tip', '\f0eb', $fa-style: 'regular');
@include mx.prompt('info', '\f06a', $rotate: 180);
@include mx.prompt('warning', '\f06a');
@include mx.prompt('danger', '\f071');
}
kbd {
font-family: Lato, sans-serif;
display: inline-block;
vertical-align: middle;
line-height: 1.3rem;
min-width: 1.75rem;
text-align: center;
margin: 0 0.3rem;
padding-top: 0.1rem;
color: var(--kbd-text-color);
background-color: var(--kbd-bg-color);
border-radius: v.$radius-sm;
border: solid 1px var(--kbd-wrap-color);
box-shadow: inset 0 -2px 0 var(--kbd-wrap-color);
}
hr {
border-color: var(--main-border-color);
opacity: 1;
}
footer {
background-color: var(--main-bg);
height: v.$footer-height;
border-top: 1px solid var(--main-border-color);
@extend %text-xs;
a {
@extend %text-highlight;
&:hover {
@extend %link-hover;
}
}
em {
@extend %text-highlight;
}
p {
text-align: center;
margin-bottom: 0;
}
}
/* fontawesome icons */
i {
&.far,
&.fas {
@extend %no-cursor;
}
}
sup {
@extend %sup-fn-target;
}
main {
line-height: 1.75;
h1 {
margin-top: 2rem;
@include bp.lg {
margin-top: 3rem;
}
}
p {
> a.popup {
&:not(.normal):not(.left):not(.right) {
@include mx.align-center;
}
}
}
.categories,
#tags,
#archives {
a:not(:hover) {
@extend %no-bottom-border;
}
}
@include bp.lte(bp.get(sm)) {
.content {
> blockquote[class^='prompt-'] {
@include mx.ml-mr(-1rem);
border-radius: 0;
max-width: none;
}
}
}
}
.footnotes > ol {
padding-left: 2rem;
margin-top: 0.5rem;
> li {
&:not(:last-child) {
margin-bottom: 0.3rem;
}
@extend %sup-fn-target;
> p {
margin-left: 0.25em;
@include mx.mt-mb(0);
}
}
}
.footnote {
@at-root a#{&} {
@include mx.ml-mr(1px);
@include mx.pl-pr(2px);
border-bottom-style: none !important;
}
}
.reversefootnote {
@at-root a#{&} {
font-size: 0.6rem;
line-height: 1;
position: relative;
bottom: 0.25em;
margin-left: 0.25em;
border-bottom-style: none !important;
}
}
/* --- Begin of Markdown table style --- */
/* it will be created by Liquid */
.table-wrapper {
overflow-x: auto;
margin-bottom: 1.5rem;
> table {
min-width: 100%;
overflow-x: auto;
border-spacing: 0;
thead {
border-bottom: solid 2px rgba(210, 215, 217, 0.75);
th {
@extend %table-cell;
}
}
tbody {
tr {
border-bottom: 1px solid var(--tb-border-color);
&:nth-child(2n) {
background-color: var(--tb-even-bg);
}
&:nth-child(2n + 1) {
background-color: var(--tb-odd-bg);
}
td {
@extend %table-cell;
}
}
} /* tbody */
} /* table */
}

View file

@ -1,164 +0,0 @@
/*
* The syntax dark mode styles.
*/
@mixin dark-syntax {
--language-border-color: #2d2d2d;
--highlight-bg-color: #151515;
--highlighter-rouge-color: #c9def1;
--highlight-lineno-color: #808080;
--inline-code-bg: rgba(255, 255, 255, 0.05);
--code-color: #b0b0b0;
--code-header-text-color: #6a6a6a;
--code-header-muted-color: #353535;
--code-header-icon-color: #565656;
--clipboard-checked-color: #2bcc2b;
--filepath-text-color: #cacaca;
.highlight .gp {
color: #87939d;
}
/* --- Syntax highlight theme from `rougify style base16.dark` --- */
.highlight table td {
padding: 5px;
}
.highlight table pre {
margin: 0;
}
.highlight,
.highlight .w {
color: #d0d0d0;
background-color: #151515;
}
.highlight .err {
color: #151515;
background-color: #ac4142;
}
.highlight .c,
.highlight .ch,
.highlight .cd,
.highlight .cm,
.highlight .cpf,
.highlight .c1,
.highlight .cs {
color: #848484;
}
.highlight .cp {
color: #f4bf75;
}
.highlight .nt {
color: #f4bf75;
}
.highlight .o,
.highlight .ow {
color: #d0d0d0;
}
.highlight .p,
.highlight .pi {
color: #d0d0d0;
}
.highlight .gi {
color: #90a959;
}
.highlight .gd {
color: #f08a8b;
background-color: #320000;
}
.highlight .gh {
color: #6a9fb5;
background-color: #151515;
font-weight: bold;
}
.highlight .k,
.highlight .kn,
.highlight .kp,
.highlight .kr,
.highlight .kv {
color: #aa759f;
}
.highlight .kc {
color: #d28445;
}
.highlight .kt {
color: #d28445;
}
.highlight .kd {
color: #d28445;
}
.highlight .s,
.highlight .sb,
.highlight .sc,
.highlight .dl,
.highlight .sd,
.highlight .s2,
.highlight .sh,
.highlight .sx,
.highlight .s1 {
color: #90a959;
}
.highlight .sa {
color: #aa759f;
}
.highlight .sr {
color: #75b5aa;
}
.highlight .si {
color: #b76d45;
}
.highlight .se {
color: #b76d45;
}
.highlight .nn {
color: #f4bf75;
}
.highlight .nc {
color: #f4bf75;
}
.highlight .no {
color: #f4bf75;
}
.highlight .na {
color: #6a9fb5;
}
.highlight .m,
.highlight .mb,
.highlight .mf,
.highlight .mh,
.highlight .mi,
.highlight .il,
.highlight .mo,
.highlight .mx {
color: #90a959;
}
.highlight .ss {
color: #90a959;
}
}

View file

@ -1,210 +0,0 @@
/*
* The syntax light mode code snippet colors.
*/
@mixin light-syntax {
/* --- custom light colors --- */
--language-border-color: #ececec;
--highlight-bg-color: #f6f8fa;
--highlighter-rouge-color: #3f596f;
--highlight-lineno-color: #9e9e9e;
--inline-code-bg: rgba(25, 25, 28, 0.05);
--code-color: #3a3a3a;
--code-header-text-color: #a3a3a3;
--code-header-muted-color: #e5e5e5;
--code-header-icon-color: #c9c8c8;
--clipboard-checked-color: #43c743;
/* --- Syntax highlight theme from `rougify style github` --- */
.highlight table td {
padding: 5px;
}
.highlight table pre {
margin: 0;
}
.highlight,
.highlight .w {
color: #24292f;
background-color: #f6f8fa;
}
.highlight .k,
.highlight .kd,
.highlight .kn,
.highlight .kp,
.highlight .kr,
.highlight .kt,
.highlight .kv {
color: #cf222e;
}
.highlight .gr {
color: #f6f8fa;
}
.highlight .gd {
color: #82071e;
background-color: #ffebe9;
}
.highlight .nb {
color: #953800;
}
.highlight .nc {
color: #953800;
}
.highlight .no {
color: #953800;
}
.highlight .nn {
color: #953800;
}
.highlight .sr {
color: #116329;
}
.highlight .na {
color: #116329;
}
.highlight .nt {
color: #116329;
}
.highlight .gi {
color: #116329;
background-color: #dafbe1;
}
.highlight .kc {
color: #0550ae;
}
.highlight .l,
.highlight .ld,
.highlight .m,
.highlight .mb,
.highlight .mf,
.highlight .mh,
.highlight .mi,
.highlight .il,
.highlight .mo,
.highlight .mx {
color: #0550ae;
}
.highlight .sb {
color: #0550ae;
}
.highlight .bp {
color: #0550ae;
}
.highlight .ne {
color: #0550ae;
}
.highlight .nl {
color: #0550ae;
}
.highlight .py {
color: #0550ae;
}
.highlight .nv,
.highlight .vc,
.highlight .vg,
.highlight .vi,
.highlight .vm {
color: #0550ae;
}
.highlight .o,
.highlight .ow {
color: #0550ae;
}
.highlight .gh {
color: #0550ae;
font-weight: bold;
}
.highlight .gu {
color: #0550ae;
font-weight: bold;
}
.highlight .s,
.highlight .sa,
.highlight .sc,
.highlight .dl,
.highlight .sd,
.highlight .s2,
.highlight .se,
.highlight .sh,
.highlight .sx,
.highlight .s1,
.highlight .ss {
color: #0a3069;
}
.highlight .nd {
color: #8250df;
}
.highlight .nf,
.highlight .fm {
color: #8250df;
}
.highlight .err {
color: #f6f8fa;
background-color: #82071e;
}
.highlight .c,
.highlight .ch,
.highlight .cd,
.highlight .cm,
.highlight .cp,
.highlight .cpf,
.highlight .c1,
.highlight .cs {
color: #68717a;
}
.highlight .gl {
color: #68717a;
}
.highlight .gt {
color: #68717a;
}
.highlight .ni {
color: #24292f;
}
.highlight .si {
color: #24292f;
}
.highlight .ge {
color: #24292f;
font-style: italic;
}
.highlight .gs {
color: #24292f;
font-weight: bold;
}
} /* light-syntax */

View file

@ -1,112 +0,0 @@
/*
* The syntax light mode typography colors
*/
@mixin light-scheme {
/* Framework color */
--main-bg: white;
--mask-bg: #c1c3c5;
--main-border-color: #f3f3f3;
/* Common color */
--text-color: #34343c;
--text-muted-color: #757575;
--text-muted-highlight-color: inherit;
--heading-color: #2a2a2a;
--label-color: #585858;
--blockquote-border-color: #eeeeee;
--blockquote-text-color: #757575;
--link-color: #0056b2;
--link-underline-color: #dee2e6;
--button-bg: #ffffff;
--btn-border-color: #e9ecef;
--btn-backtotop-color: #686868;
--btn-backtotop-border-color: #f1f1f1;
--btn-box-shadow: #eaeaea;
--checkbox-color: #c5c5c5;
--checkbox-checked-color: #07a8f7;
--img-bg: radial-gradient(
circle,
rgb(255, 255, 255) 0%,
rgb(239, 239, 239) 100%
);
--shimmer-bg: linear-gradient(
90deg,
rgba(250, 250, 250, 0) 0%,
rgba(232, 230, 230, 1) 50%,
rgba(250, 250, 250, 0) 100%
);
/* Sidebar */
--site-title-color: rgb(113, 113, 113);
--site-subtitle-color: #717171;
--sidebar-bg: #f6f8fa;
--sidebar-border-color: #efefef;
--sidebar-muted-color: #545454;
--sidebar-active-color: #1d1d1d;
--sidebar-hover-bg: rgb(223, 233, 241, 0.64);
--sidebar-btn-bg: white;
--sidebar-btn-color: #8e8e8e;
--avatar-border-color: white;
/* Topbar */
--topbar-bg: rgb(255, 255, 255, 0.7);
--topbar-text-color: rgb(78, 78, 78);
--search-border-color: rgb(240, 240, 240);
--search-icon-color: #c2c6cc;
--input-focus-border-color: #b8b8b8;
/* Home page */
--post-list-text-color: dimgray;
--btn-patinator-text-color: #555555;
--btn-paginator-hover-color: var(--sidebar-bg);
/* Posts */
--toc-highlight: #0550ae;
--btn-share-color: gray;
--btn-share-hover-color: #0d6efd;
--card-bg: white;
--card-hovor-bg: #e2e2e2;
--card-shadow: rgb(104, 104, 104, 0.05) 0 2px 6px 0,
rgba(211, 209, 209, 0.15) 0 0 0 1px;
--footnote-target-bg: lightcyan;
--tb-odd-bg: #fbfcfd;
--tb-border-color: #eaeaea;
--dash-color: silver;
--kbd-wrap-color: #bdbdbd;
--kbd-text-color: var(--text-color);
--kbd-bg-color: white;
--prompt-text-color: rgb(46, 46, 46, 0.77);
--prompt-tip-bg: rgb(123, 247, 144, 0.2);
--prompt-tip-icon-color: #03b303;
--prompt-info-bg: #e1f5fe;
--prompt-info-icon-color: #0070cb;
--prompt-warning-bg: rgb(255, 243, 205);
--prompt-warning-icon-color: #ef9c03;
--prompt-danger-bg: rgb(248, 215, 218, 0.56);
--prompt-danger-icon-color: #df3c30;
/* Tags */
--tag-border: #dee2e6;
--tag-shadow: var(--btn-border-color);
--tag-hover: rgb(222, 226, 230);
--search-tag-bg: #f8f9fa;
/* Categories */
--categories-border: rgba(0, 0, 0, 0.125);
--categories-hover-bg: var(--btn-border-color);
--categories-icon-hover-color: darkslategray;
/* Archive */
--timeline-color: rgba(0, 0, 0, 0.075);
--timeline-node-bg: #c2c6cc;
--timeline-year-dot-color: #ffffff;
[class^='prompt-'] {
--link-underline-color: rgb(219, 216, 216);
}
.dark {
display: none;
}
} /* light-scheme */

View file

@ -0,0 +1,51 @@
@use '../abstracts/variables' as v;
@use '../abstracts/breakpoints' as bp;
#back-to-top {
visibility: hidden;
opacity: 0;
z-index: 1;
cursor: pointer;
position: fixed;
right: 1rem;
bottom: calc(v.$footer-height-large - v.$back2top-size / 2);
background: var(--button-bg);
color: var(--btn-backtotop-color);
padding: 0;
width: v.$back2top-size;
height: v.$back2top-size;
border-radius: 50%;
border: 1px solid var(--btn-backtotop-border-color);
transition: opacity 0.5s ease-in-out, transform 0.2s ease-out;
@include bp.lg {
right: 5%;
bottom: calc(v.$footer-height - v.$back2top-size / 2);
}
@include bp.xxl {
right: calc((100vw - v.$sidebar-width - 1140px) / 2 + 3rem);
}
@include bp.xxxl {
right: calc(
(100vw - v.$sidebar-width-large - v.$main-content-max-width) / 2 + 2rem
);
}
&:hover {
transform: translate3d(0, -5px, 0);
-webkit-transform: translate3d(0, -5px, 0);
}
i {
line-height: v.$back2top-size;
position: relative;
bottom: 2px;
}
&.show {
opacity: 1;
visibility: visible;
}
}

View file

@ -0,0 +1,2 @@
@forward 'buttons';
@forward 'popups';

View file

@ -0,0 +1,172 @@
@use '../abstracts/variables' as v;
@use '../abstracts/breakpoints' as bp;
@use '../abstracts/placeholders';
/* PWA update popup */
#notification {
@-webkit-keyframes popup {
from {
opacity: 0;
bottom: 0;
}
}
@keyframes popup {
from {
opacity: 0;
bottom: 0;
}
}
.toast-header {
background: none;
border-bottom: none;
color: inherit;
}
.toast-body {
font-family: Lato, sans-serif;
line-height: 1.25rem;
button {
font-size: 90%;
min-width: 4rem;
}
}
&.toast {
&.show {
display: block;
min-width: 20rem;
border-radius: 0.5rem;
-webkit-backdrop-filter: blur(10px);
backdrop-filter: blur(10px);
background-color: rgba(255, 255, 255, 0.5);
color: #1b1b1eba;
position: fixed;
left: 50%;
bottom: 20%;
transform: translateX(-50%);
-webkit-animation: popup 0.8s;
animation: popup 0.8s;
}
}
}
#toc-popup {
$slide-in: slide-in 0.3s ease-out;
$slide-out: slide-out 0.3s ease-out;
$curtain-height: 2rem;
$backdrop: blur(5px);
border-color: var(--toc-popup-border-color);
border-width: 1px;
border-radius: v.$radius-lg;
color: var(--text-color);
background: var(--card-bg);
margin-top: v.$topbar-height;
min-width: 20rem;
font-size: 1.05rem;
@include bp.sm {
max-width: 32rem;
}
&[open] {
-webkit-animation: $slide-in;
animation: $slide-in;
}
&[closing] {
-webkit-animation: $slide-out;
animation: $slide-out;
}
@include bp.lg {
left: v.$sidebar-width;
}
.header {
@extend %btn-color;
position: -webkit-sticky;
position: sticky;
top: 0;
background-color: inherit;
border-bottom: 1px solid var(--main-border-color);
.label {
font-family: v.$font-family-heading;
}
}
button {
> i {
font-size: 1.25rem;
vertical-align: middle;
}
&:focus-visible {
box-shadow: none;
}
}
ul {
list-style-type: none;
padding-left: 0;
li {
ul,
& + li {
margin-top: 0.25rem;
}
a {
display: flex;
line-height: 1.5;
padding: 0.375rem 0;
padding-right: 1.125rem;
&.toc-link::before {
display: none;
}
}
}
}
@for $i from 2 through 4 {
.node-name--H#{$i} {
padding-left: 1.125rem * ($i - 1);
}
}
.is-active-link {
color: var(--toc-highlight) !important;
font-weight: 600;
}
&::-webkit-backdrop {
-webkit-backdrop-filter: $backdrop;
backdrop-filter: $backdrop;
}
&::backdrop {
-webkit-backdrop-filter: $backdrop;
backdrop-filter: $backdrop;
}
&::after {
display: flex;
content: '';
position: relative;
background: linear-gradient(transparent, var(--card-bg) 70%);
height: $curtain-height;
}
#toc-popup-content {
overflow: auto;
max-height: calc(100vh - 4 * v.$topbar-height);
font-family: v.$font-family-heading;
margin-bottom: -$curtain-height;
}
}

36
_sass/layout/_footer.scss Normal file
View file

@ -0,0 +1,36 @@
@use '../abstracts/breakpoints' as bp;
@use '../abstracts/variables' as v;
@use '../abstracts/mixins' as mx;
@use '../abstracts/placeholders';
footer {
background-color: var(--main-bg);
height: v.$footer-height;
border-top: 1px solid var(--main-border-color);
@extend %text-xs;
@include bp.lt(bp.get(lg)) {
@include mx.slide;
height: v.$footer-height-large;
padding: 1.5rem 0;
}
a {
@extend %text-highlight;
&:hover {
@extend %link-hover;
}
}
em {
@extend %text-highlight;
}
p {
text-align: center;
margin-bottom: 0;
}
}

4
_sass/layout/_index.scss Normal file
View file

@ -0,0 +1,4 @@
@forward 'sidebar';
@forward 'topbar';
@forward 'panel';
@forward 'footer';

70
_sass/layout/_panel.scss Normal file
View file

@ -0,0 +1,70 @@
@use '../abstracts/breakpoints' as bp;
@use '../abstracts/mixins' as mx;
@use '../abstracts/placeholders';
.access {
top: 2rem;
transition: top 0.2s ease-in-out;
margin-top: 3rem;
&:only-child {
position: -webkit-sticky;
position: sticky;
}
> section {
@extend %panel-border;
padding-left: 1rem;
&:not(:first-child) {
margin-top: 4rem;
}
}
.content {
font-size: 0.9rem;
}
}
#panel-wrapper {
/* the headings */
.panel-heading {
font-family: inherit;
line-height: inherit;
@include mx.label(inherit);
}
.post-tag {
line-height: 1.05rem;
font-size: 0.85rem;
border-radius: 0.8rem;
padding: 0.3rem 0.5rem;
margin: 0 0.35rem 0.5rem 0;
&:hover {
transition: all 0.3s ease-in;
}
}
> :last-child {
margin-bottom: 4rem;
}
@include bp.lt(bp.get(xl)) {
display: none;
}
}
#access-lastmod {
a {
color: inherit;
&:hover {
@extend %link-hover;
}
@extend %no-bottom-border;
}
}

258
_sass/layout/_sidebar.scss Normal file
View file

@ -0,0 +1,258 @@
@use '../abstracts/variables' as v;
@use '../abstracts/mixins' as mx;
@use '../abstracts/breakpoints' as bp;
@use '../abstracts/placeholders';
$btn-border-width: 3px;
$btn-mb: 0.5rem;
$sidebar-display: 'sidebar-display'; /* the attribute for sidebar display */
#sidebar {
@include mx.pl-pr(0);
position: fixed;
top: 0;
left: 0;
height: 100%;
overflow-y: auto;
width: v.$sidebar-width;
background: var(--sidebar-bg);
border-right: 1px solid var(--sidebar-border-color);
/* Hide scrollbar for IE, Edge and Firefox */
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
/* Hide scrollbar for Chrome, Safari and Opera */
&::-webkit-scrollbar {
display: none;
}
@include bp.lt(bp.get(lg)) {
@include mx.slide;
transform: translateX(-#{v.$sidebar-width}); /* hide */
-webkit-transform: translateX(-#{v.$sidebar-width});
[#{$sidebar-display}] & {
transform: translateX(0);
}
}
@include bp.xxxl {
width: v.$sidebar-width-large;
}
%sidebar-link-hover {
&:hover {
color: var(--sidebar-active-color);
}
}
a {
@extend %sidebar-links;
}
#avatar {
display: block;
width: 6.5rem;
height: 6.5rem;
overflow: hidden;
box-shadow: var(--avatar-border-color) 0 0 0 2px;
transform: translateZ(0); /* fixed the zoom in Safari */
@include bp.sm {
width: 7rem;
height: 7rem;
}
img {
transition: transform 0.5s;
&:hover {
transform: scale(1.2);
}
}
}
.profile-wrapper {
@include mx.mt-mb(2.5rem);
@extend %clickable-transition;
padding-left: 2.5rem;
padding-right: 1.25rem;
width: 100%;
@include bp.lg {
margin-top: 3rem;
}
@include bp.xxxl {
margin-top: 3.5rem;
margin-bottom: 2.5rem;
padding-left: 3.5rem;
}
}
.site-title {
@extend %clickable-transition;
@extend %sidebar-link-hover;
font-family: inherit;
font-weight: 900;
font-size: 1.75rem;
line-height: 1.2;
letter-spacing: 0.25px;
margin-top: 1.25rem;
margin-bottom: 0.5rem;
width: fit-content;
color: var(--site-title-color);
}
.site-subtitle {
font-size: 95%;
color: var(--site-subtitle-color);
margin-top: 0.25rem;
word-spacing: 1px;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
ul {
margin-bottom: 2rem;
li.nav-item {
opacity: 0.9;
width: 100%;
@include mx.pl-pr(1.5rem);
@include bp.xxxl {
@include mx.pl-pr(2.75rem);
}
a.nav-link {
@include mx.pt-pb(0.6rem);
display: flex;
align-items: center;
border-radius: 0.75rem;
font-weight: 600;
&:hover {
background-color: var(--sidebar-hover-bg);
}
i {
font-size: 95%;
opacity: 0.8;
margin-right: 1.5rem;
}
span {
font-size: 90%;
letter-spacing: 0.2px;
}
}
&.active {
.nav-link {
color: var(--sidebar-active-color);
background-color: var(--sidebar-hover-bg);
span {
opacity: 1;
}
}
}
&:not(:first-child) {
margin-top: 0.25rem;
}
}
}
.sidebar-bottom {
padding-left: 2rem;
padding-right: 1rem;
margin-bottom: 1.5rem;
@include bp.xxxl {
padding-left: 2.75rem;
margin-bottom: 1.75rem;
}
$btn-size: 1.75rem;
%button {
width: $btn-size;
height: $btn-size;
margin-bottom: $btn-mb; // multi line gap
border-radius: 50%;
color: var(--sidebar-btn-color);
background-color: var(--sidebar-btn-bg);
text-align: center;
display: flex;
align-items: center;
justify-content: center;
&:not(:focus-visible) {
box-shadow: var(--sidebar-border-color) 0 0 0 1px;
}
&:hover {
background-color: var(--sidebar-hover-bg);
}
}
a {
@extend %button;
@extend %sidebar-link-hover;
@extend %clickable-transition;
&:not(:last-child) {
margin-right: v.$sb-btn-gap;
@include bp.xxxl {
margin-right: v.$sb-btn-gap-lg;
}
}
}
i {
line-height: $btn-size;
}
#mode-toggle {
@extend %button;
@extend %sidebar-links;
@extend %sidebar-link-hover;
}
.icon-border {
@extend %no-cursor;
@include mx.ml-mr(calc((v.$sb-btn-gap - $btn-border-width) / 2));
background-color: var(--sidebar-btn-color);
content: '';
width: $btn-border-width;
height: $btn-border-width;
border-radius: 50%;
margin-bottom: $btn-mb;
@include bp.xxxl {
@include mx.ml-mr(calc((v.$sb-btn-gap-lg - $btn-border-width) / 2));
}
}
} /* .sidebar-bottom */
} /* #sidebar */
[#{$sidebar-display}] {
#main-wrapper {
@include bp.lt(bp.get(lg)) {
transform: translateX(v.$sidebar-width);
}
}
}

86
_sass/layout/_topbar.scss Normal file
View file

@ -0,0 +1,86 @@
@use '../abstracts/variables' as v;
@use '../abstracts/mixins' as mx;
@use '../abstracts/breakpoints' as bp;
@use '../abstracts/placeholders';
#topbar-wrapper {
height: v.$topbar-height;
background-color: var(--topbar-bg);
@include bp.lt(bp.get(lg)) {
@include mx.slide(top 0.2s ease);
left: 0;
}
}
#topbar {
@extend %btn-color;
#breadcrumb {
font-size: 1rem;
color: var(--text-muted-color);
padding-left: 0.5rem;
a:hover {
@extend %link-hover;
}
span {
&:not(:last-child) {
&::after {
content: '';
padding: 0 0.3rem;
}
}
}
@include bp.lt(bp.get(lg)) {
display: none;
}
@include bp.between(bp.get(lg), calc(#{bp.get(xl)} - 1px)) {
width: 65%;
overflow: hidden;
text-overflow: ellipsis;
word-break: keep-all;
white-space: nowrap;
}
}
@include bp.lte(bp.get(md)) {
@include mx.max-w-100;
}
@include bp.lt(bp.get(lg)) {
max-width: 100%;
}
}
#topbar-title {
display: none;
font-size: 1.1rem;
font-weight: 600;
font-family: sans-serif;
color: var(--topbar-text-color);
text-align: center;
width: 70%;
word-break: keep-all;
@include bp.lt(bp.get(lg)) {
display: block;
}
@include bp.lg {
text-align: left;
}
}
#sidebar-trigger,
#search-trigger {
display: none;
@include bp.lt(bp.get(lg)) {
display: block;
}
}

View file

@ -1,2 +1,2 @@
@import 'dist/bootstrap'; @use 'vendors/bootstrap';
@import 'main'; @use 'main';

View file

@ -1,13 +1,4 @@
@import 'colors/typography-light'; @forward 'base';
@import 'colors/typography-dark'; @forward 'components';
@import 'addon/variables'; @forward 'layout';
@import 'variables-hook'; @forward 'pages';
@import 'addon/module';
@import 'addon/syntax';
@import 'addon/commons';
@import 'layout/home';
@import 'layout/post';
@import 'layout/tags';
@import 'layout/archives';
@import 'layout/categories';
@import 'layout/category-tag';

View file

@ -1,10 +1,17 @@
/* @use '../abstracts/breakpoints' as bp;
Style for Archives @use '../abstracts/placeholders';
*/
#archives { #archives {
letter-spacing: 0.03rem; letter-spacing: 0.03rem;
@include bp.lt(bp.get(sm)) {
margin-top: -1rem;
ul {
letter-spacing: 0;
}
}
$timeline-width: 4px; $timeline-width: 4px;
%timeline { %timeline {
@ -58,9 +65,8 @@
li { li {
font-size: 1.1rem; font-size: 1.1rem;
line-height: 3rem; line-height: 3rem;
white-space: nowrap;
overflow: hidden; @extend %text-ellipsis;
text-overflow: ellipsis;
&:nth-child(odd) { &:nth-child(odd) {
background-color: var(--main-bg, #ffffff); background-color: var(--main-bg, #ffffff);
@ -132,13 +138,3 @@
} }
} }
} /* #archives */ } /* #archives */
@media all and (max-width: 576px) {
#archives {
margin-top: -1rem;
ul {
letter-spacing: 0;
}
}
}

View file

@ -1,8 +1,7 @@
/* @use '../abstracts/variables' as v;
Style for Tab Categories @use '../abstracts/placeholders';
*/
%category-icon-color { %-category-icon-color {
color: gray; color: gray;
} }
@ -16,7 +15,7 @@
} }
.card-header { .card-header {
$radius: calc($radius-lg - 1px); $radius: calc(v.$radius-lg - 1px);
padding: 0.75rem; padding: 0.75rem;
border-radius: $radius; border-radius: $radius;
@ -29,7 +28,7 @@
} }
i { i {
@extend %category-icon-color; @extend %-category-icon-color;
font-size: 86%; /* fontawesome icons */ font-size: 86%; /* fontawesome icons */
} }

View file

@ -1,6 +1,6 @@
/* @use '../abstracts/breakpoints' as bp;
Style for page Category and Tag @use '../abstracts/mixins' as mx;
*/ @use '../abstracts/placeholders';
.dash { .dash {
margin: 0 0.5rem 0.6rem 0.5rem; margin: 0 0.5rem 0.6rem 0.5rem;
@ -24,6 +24,10 @@
position: relative; position: relative;
top: 0.6rem; top: 0.6rem;
margin-right: 0.5rem; margin-right: 0.5rem;
@include bp.lt(bp.get(sm)) {
margin: 0 0.5rem;
}
} }
/* post's title */ /* post's title */
@ -31,6 +35,10 @@
@extend %no-bottom-border; @extend %no-bottom-border;
font-size: 1.1rem; font-size: 1.1rem;
@include bp.lt(bp.get(sm)) {
@include mx.text-ellipsis;
}
} }
} }
} }
@ -53,20 +61,3 @@
margin-bottom: -1px; /* Avoid jumping */ margin-bottom: -1px; /* Avoid jumping */
} }
} }
@media all and (max-width: 576px) {
#page-category,
#page-tag {
ul > li {
&::before {
margin: 0 0.5rem;
}
> a {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
}
}

View file

@ -1,10 +1,14 @@
/* @use '../abstracts/variables' as v;
Style for Homepage @use '../abstracts/breakpoints' as bp;
*/ @use '../abstracts/placeholders';
#post-list { #post-list {
margin-top: 2rem; margin-top: 2rem;
@include bp.lg {
margin-top: 2.5rem;
}
.card-wrapper { .card-wrapper {
&:hover { &:hover {
text-decoration: none; text-decoration: none;
@ -20,7 +24,11 @@
background: none; background: none;
%img-radius { %img-radius {
border-radius: $radius-lg $radius-lg 0 0; border-radius: v.$radius-lg v.$radius-lg 0 0;
@include bp.md {
border-radius: 0 v.$radius-lg v.$radius-lg 0;
}
} }
.preview-img { .preview-img {
@ -35,6 +43,10 @@
height: 100%; height: 100%;
padding: 1rem; padding: 1rem;
@include bp.md {
padding: 1.75rem 1.75rem 1.25rem;
}
.card-title { .card-title {
@extend %text-clip; @extend %text-clip;
@ -46,14 +58,20 @@
color: var(--text-muted-color) !important; color: var(--text-muted-color) !important;
} }
.card-text.content { .card-text {
@extend %muted; @include bp.md {
display: inherit !important;
}
p { &.content {
@extend %text-clip; @extend %muted;
line-height: 1.5; p {
margin: 0; @extend %text-clip;
line-height: 1.5;
margin: 0;
}
} }
} }
@ -63,6 +81,10 @@
i { i {
&:not(:first-child) { &:not(:first-child) {
margin-left: 1.5rem; margin-left: 1.5rem;
@include bp.md {
margin-left: 1.75rem;
}
} }
} }
@ -74,9 +96,8 @@
> div:first-child { > div:first-child {
display: block; display: block;
white-space: nowrap;
overflow: hidden; @extend %text-ellipsis;
text-overflow: ellipsis;
} }
} }
} }
@ -88,11 +109,28 @@
font-family: Lato, sans-serif; font-family: Lato, sans-serif;
justify-content: space-evenly; justify-content: space-evenly;
@include bp.lg {
font-size: 0.85rem;
justify-content: center;
}
a:hover { a:hover {
text-decoration: none; text-decoration: none;
} }
.page-item { .page-item {
@include bp.lt(bp.get(lg)) {
&:not(:first-child):not(:last-child) {
display: none;
}
}
@include bp.lg {
&:not(:last-child) {
margin-right: 0.7rem;
}
}
.page-link { .page-link {
color: var(--btn-patinator-text-color); color: var(--btn-patinator-text-color);
padding: 0 0.6rem; padding: 0 0.6rem;
@ -126,64 +164,10 @@
} }
} }
} /* .page-item */ } /* .page-item */
} /* .pagination */
/* Tablet */ .page-index {
@media all and (min-width: 768px) { @include bp.lg {
%img-radius {
border-radius: 0 $radius-lg $radius-lg 0;
}
#post-list {
.card {
.card-body {
padding: 1.75rem 1.75rem 1.25rem 1.75rem;
.card-text {
display: inherit !important;
}
.post-meta {
i {
&:not(:first-child) {
margin-left: 1.75rem;
}
}
}
}
}
}
}
/* Hide SideBar and TOC */
@media all and (max-width: 830px) {
.pagination {
.page-item {
&:not(:first-child):not(:last-child) {
display: none;
}
}
}
}
/* Sidebar is visible */
@media all and (min-width: 831px) {
#post-list {
margin-top: 2.5rem;
}
.pagination {
font-size: 0.85rem;
justify-content: center;
.page-item {
&:not(:last-child) {
margin-right: 0.7rem;
}
}
.page-index {
display: none; display: none;
} }
} /* .pagination */ }
} }

7
_sass/pages/_index.scss Normal file
View file

@ -0,0 +1,7 @@
@forward 'search';
@forward 'home';
@forward 'post';
@forward 'categories';
@forward 'tags';
@forward 'archives';
@forward 'category-tag';

View file

@ -1,14 +1,15 @@
/* @use '../abstracts/variables' as v;
Post-specific style @use '../abstracts/breakpoints' as bp;
*/ @use '../abstracts/mixins' as mx;
@use '../abstracts/placeholders';
%btn-post-nav { %-btn-post-nav {
width: 50%; width: 50%;
position: relative; position: relative;
border-color: var(--btn-border-color); border-color: var(--btn-border-color);
} }
@mixin dot($pl: 0.25rem, $pr: 0.25rem) { @mixin -dot($pl: 0.25rem, $pr: 0.25rem) {
content: '\2022'; content: '\2022';
padding-left: $pl; padding-left: $pl;
padding-right: $pr; padding-right: $pr;
@ -24,7 +25,7 @@ header {
.post-meta { .post-meta {
span + span::before { span + span::before {
@include dot; @include -dot;
} }
em, em,
@ -97,7 +98,7 @@ header {
&:hover { &:hover {
i { i {
@extend %btn-share-hovor; @extend %btn-share-hover;
} }
} }
} }
@ -113,9 +114,20 @@ header {
} /* .share-wrapper */ } /* .share-wrapper */
} }
.post-tail-bottom {
@include bp.lte(bp.get(sm)) {
flex-wrap: wrap-reverse !important;
> div:first-child {
width: 100%;
margin-top: 1rem;
}
}
}
.share-mastodon { .share-mastodon {
/* See: https://github.com/justinribeiro/share-to-mastodon#properties */ /* See: https://github.com/justinribeiro/share-to-mastodon#properties */
--wc-stm-font-family: $font-family-base; --wc-stm-font-family: v.$font-family-base;
--wc-stm-dialog-background-color: var(--card-bg); --wc-stm-dialog-background-color: var(--card-bg);
--wc-stm-form-button-border: 1px solid var(--btn-border-color); --wc-stm-form-button-border: 1px solid var(--btn-border-color);
--wc-stm-form-submit-background-color: var(--sidebar-btn-bg); --wc-stm-form-submit-background-color: var(--sidebar-btn-bg);
@ -139,8 +151,13 @@ header {
} }
.post-navigation { .post-navigation {
@include bp.lt(bp.get(lg)) {
@include mx.pl-pr(0);
@include mx.ml-mr(-0.5rem);
}
.btn { .btn {
@extend %btn-post-nav; @extend %-btn-post-nav;
&:not(:hover) { &:not(:hover) {
color: var(--link-color); color: var(--link-color);
@ -153,7 +170,7 @@ header {
} }
&.disabled { &.disabled {
@extend %btn-post-nav; @extend %-btn-post-nav;
pointer-events: auto; pointer-events: auto;
cursor: not-allowed; cursor: not-allowed;
@ -173,12 +190,12 @@ header {
} }
&:first-child { &:first-child {
border-radius: $radius-lg 0 0 $radius-lg; border-radius: v.$radius-lg 0 0 v.$radius-lg;
left: 0.5px; left: 0.5px;
} }
&:last-child { &:last-child {
border-radius: 0 $radius-lg $radius-lg 0; border-radius: 0 v.$radius-lg v.$radius-lg 0;
right: 0.5px; right: 0.5px;
} }
} }
@ -228,14 +245,27 @@ header {
} }
} }
#toc-wrapper { /* TOC panel */
border-left: 1px solid rgba(158, 158, 158, 0.17);
position: -webkit-sticky; %top-cover {
content: '';
display: block;
position: sticky; position: sticky;
top: 4rem; top: 0;
width: 100%;
height: 3rem;
background: linear-gradient(var(--main-bg) 50%, transparent);
}
#toc-wrapper {
top: 0;
transition: top 0.2s ease-in-out; transition: top 0.2s ease-in-out;
-webkit-animation: fade-up 0.8s; -webkit-animation: fade-up 0.8s;
animation: fade-up 0.8s; animation: fade-up 0.8s;
overflow-y: auto;
max-height: calc(100vh - 2rem);
scrollbar-width: none;
margin-top: 2rem;
ul { ul {
list-style: none; list-style: none;
@ -248,6 +278,10 @@ header {
margin: 0.4rem 0; margin: 0.4rem 0;
} }
&:first-child {
margin-top: 0;
}
a { a {
padding: 0.2rem 0 0.2rem 1.25rem; padding: 0.2rem 0 0.2rem 1.25rem;
} }
@ -257,9 +291,8 @@ header {
.toc-link { .toc-link {
display: block; display: block;
white-space: nowrap;
overflow: hidden; @extend %text-ellipsis;
text-overflow: ellipsis;
&:hover { &:hover {
color: var(--toc-highlight); color: var(--toc-highlight);
@ -278,7 +311,6 @@ header {
&::before { &::before {
display: inline-block; display: inline-block;
width: 1px; width: 1px;
left: -1px;
height: 1.25rem; height: 1.25rem;
background-color: var(--toc-highlight) !important; background-color: var(--toc-highlight) !important;
} }
@ -288,13 +320,130 @@ header {
padding-left: 0.75rem; padding-left: 0.75rem;
} }
} }
@at-root .toc-border-cover {
@extend %top-cover;
margin-bottom: -4rem;
}
&::before {
@extend %top-cover;
}
&::after {
content: '';
position: fixed;
bottom: 0;
width: 15%;
height: 3.25rem;
margin-left: -1px;
background: linear-gradient(transparent, var(--main-bg) 70%);
}
> * {
@extend %panel-border;
}
}
/* --- TOC button, bar and popup in mobile/tablet --- */
#toc-bar {
position: -webkit-sticky;
position: sticky;
top: 0;
z-index: 1;
margin: 0 -1rem;
height: v.$topbar-height;
background: var(--main-bg);
border-bottom: 1px solid var(--main-border-color);
transition: all 0.2s ease-in-out;
@extend %btn-color;
@include bp.xl {
display: none !important;
}
.label {
@extend %heading;
margin-left: 0.375rem;
padding: 0 0.75rem;
color: inherit;
}
&.invisible {
top: -#{v.$topbar-height};
transition: none;
}
}
#toc-solo-trigger {
color: var(--text-muted-color);
border-color: var(--btn-border-color);
border-radius: v.$radius-lg;
@include bp.xl {
display: none !important;
}
.label {
font-size: 1rem;
font-family: v.$font-family-heading;
}
&:hover {
box-shadow: none;
background: none;
}
}
@mixin slide-in {
from {
opacity: 0.7;
transform: translateY(-#{v.$topbar-height});
}
to {
opacity: 1;
transform: translateY(0);
}
}
@mixin slide-out {
0% {
transform: translateY(0);
opacity: 1;
}
100% {
transform: translateY(-#{v.$topbar-height});
opacity: 0;
}
}
@-webkit-keyframes slide-in {
@include slide-in;
}
@keyframes slide-in {
@include slide-in;
}
@-webkit-keyframes slide-out {
@include slide-out;
}
@keyframes slide-out {
@include slide-out;
} }
/* --- Related Posts --- */ /* --- Related Posts --- */
#related-posts { #related-posts {
> h3 { > h3 {
@include label(1.1rem, 600); @include mx.label(1.1rem, 600);
} }
time { time {
@ -305,10 +454,11 @@ header {
} }
p { p {
@extend %text-ellipsis;
font-size: 0.9rem; font-size: 0.9rem;
margin-bottom: 0.5rem; margin-bottom: 0.5rem;
overflow: hidden; white-space: break-spaces;
text-overflow: ellipsis;
display: -webkit-box; display: -webkit-box;
-webkit-line-clamp: 2; -webkit-line-clamp: 2;
-webkit-box-orient: vertical; -webkit-box-orient: vertical;
@ -328,43 +478,31 @@ header {
.utterances { .utterances {
max-width: 100%; max-width: 100%;
min-height: 269px;
} }
%btn-share-hovor { %btn-share-hover {
color: var(--btn-share-hover-color) !important; color: var(--btn-share-hover-color) !important;
} }
.share-label { .share-label {
@include label(inherit, 400, inherit); @include mx.label(inherit, 400, inherit);
&::after { &::after {
content: ':'; content: ':';
} }
} }
@media all and (max-width: 576px) { .content > p > img {
.post-tail-bottom { @include bp.lte(bp.get(md)) {
flex-wrap: wrap-reverse !important;
> div:first-child {
width: 100%;
margin-top: 1rem;
}
}
}
@media all and (max-width: 768px) {
.content > p > img {
max-width: calc(100% + 1rem); max-width: calc(100% + 1rem);
} }
} }
/* Hide SideBar and TOC */ h2,
@media all and (max-width: 849px) { h3,
.post-navigation { h4 {
padding-left: 0; @include bp.xl {
padding-right: 0; scroll-margin-top: 2rem;
margin-left: -0.5rem;
margin-right: -0.5rem;
} }
} }

184
_sass/pages/_search.scss Normal file
View file

@ -0,0 +1,184 @@
@use '../abstracts/breakpoints' as bp;
@use '../abstracts/variables' as v;
@use '../abstracts/placeholders';
search {
display: flex;
width: 100%;
border-radius: 1rem;
border: 1px solid var(--search-border-color);
background: var(--main-bg);
padding: 0 0.5rem;
i {
z-index: 2;
font-size: 0.9rem;
color: var(--search-icon-color);
}
@include bp.lt(bp.get(lg)) {
display: none;
}
@include bp.lg {
max-width: v.$search-max-width;
}
@include bp.xl {
margin-right: 4rem;
}
@include bp.xxxl {
margin-right: calc(
v.$main-content-max-width / 4 - v.$search-max-width - 0.75rem
);
}
}
#search-result-wrapper {
display: none;
height: 100%;
width: 100%;
overflow: auto;
.content {
margin-top: 2rem;
}
@include bp.lt(bp.get(lg)) {
width: 100%;
.content {
letter-spacing: 0;
}
}
@include bp.lg {
max-width: v.$main-content-max-width;
justify-content: start !important;
}
}
#search-results {
padding-bottom: 3rem;
@include bp.between(bp.get(lg), calc(#{bp.get(xl)} - 1px)) {
> div {
max-width: 700px;
}
}
a {
font-size: 1.4rem;
line-height: 1.5rem;
&:hover {
@extend %link-hover;
}
@extend %link-color;
@extend %no-bottom-border;
@extend %heading;
}
> article {
width: 100%;
&:not(:last-child) {
margin-bottom: 1rem;
}
@include bp.xl {
width: 45%;
&:nth-child(odd) {
margin-right: 1.5rem;
}
&:nth-child(even) {
margin-left: 1.5rem;
}
&:last-child:nth-child(odd) {
position: relative;
right: 24.3%;
}
}
h2 {
line-height: 2.5rem;
}
/* icons */
i {
color: #818182;
margin-right: 0.15rem;
font-size: 80%;
}
> p {
@extend %text-ellipsis;
white-space: break-spaces;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
}
}
}
/* 'Cancel' link */
#search-cancel {
color: var(--link-color);
display: none;
white-space: nowrap;
@extend %cursor-pointer;
}
#search-input {
background: center;
border: 0;
border-radius: 0;
padding: 0.18rem 0.3rem;
color: var(--text-color);
height: auto;
&:focus {
box-shadow: none;
}
@include bp.xl {
transition: all 0.3s ease-in-out;
}
}
#search-hints {
padding: 0 1rem;
@include bp.lg {
display: none;
}
h4 {
margin-bottom: 1.5rem;
}
.post-tag {
display: inline-block;
line-height: 1rem;
font-size: 1rem;
background: var(--search-tag-bg);
border: none;
padding: 0.5rem;
margin: 0 1.25rem 1rem 0;
&::before {
content: '#';
color: var(--text-muted-color);
padding-right: 0.2rem;
}
@extend %link-color;
}
}

View file

@ -1,6 +1,4 @@
/* @use '../abstracts/breakpoints' as bp;
Styles for Tab Tags
*/
.tag { .tag {
border-radius: 0.7em; border-radius: 0.7em;
@ -17,3 +15,9 @@
font-family: Oswald, sans-serif; font-family: Oswald, sans-serif;
} }
} }
#tags {
@include bp.lt(bp.get(lg)) {
justify-content: center !important;
}
}

View file

@ -1,8 +1,6 @@
/* @mixin styles {
* The main dark mode styles color-scheme: dark;
*/
@mixin dark-scheme {
/* Framework color */ /* Framework color */
--main-bg: rgb(27, 27, 30); --main-bg: rgb(27, 27, 30);
--mask-bg: rgb(68, 69, 70); --mask-bg: rgb(68, 69, 70);
@ -22,7 +20,6 @@
--btn-border-color: #2e2f31; --btn-border-color: #2e2f31;
--btn-backtotop-color: var(--text-color); --btn-backtotop-color: var(--text-color);
--btn-backtotop-border-color: #212122; --btn-backtotop-border-color: #212122;
--btn-box-shadow: var(--main-bg);
--card-header-bg: #292929; --card-header-bg: #292929;
--checkbox-color: rgb(118, 120, 121); --checkbox-color: rgb(118, 120, 121);
--checkbox-checked-color: var(--link-color); --checkbox-checked-color: var(--link-color);
@ -60,6 +57,7 @@
/* Posts */ /* Posts */
--toc-highlight: rgb(116, 178, 243); --toc-highlight: rgb(116, 178, 243);
--toc-popup-border-color: #373737;
--tag-hover: rgb(43, 56, 62); --tag-hover: rgb(43, 56, 62);
--tb-odd-bg: #252526; /* odd rows of the posts' table */ --tb-odd-bg: #252526; /* odd rows of the posts' table */
--tb-even-bg: rgb(31, 31, 34); /* even rows of the posts' table */ --tb-even-bg: rgb(31, 31, 34); /* even rows of the posts' table */
@ -100,7 +98,18 @@
--timeline-color: rgb(63, 65, 68); --timeline-color: rgb(63, 65, 68);
--timeline-year-dot-color: var(--timeline-color); --timeline-year-dot-color: var(--timeline-color);
color-scheme: dark; /* Code highlight colors */
--language-border-color: #2d2d2d;
--highlight-bg-color: #151515;
--highlighter-rouge-color: #c9def1;
--highlight-lineno-color: #808080;
--inline-code-bg: rgba(255, 255, 255, 0.05);
--code-color: #b0b0b0;
--code-header-text-color: #6a6a6a;
--code-header-muted-color: #353535;
--code-header-icon-color: #565656;
--clipboard-checked-color: #2bcc2b;
--filepath-text-color: #cacaca;
.light { .light {
display: none; display: none;
@ -144,4 +153,151 @@
#disqus_thread { #disqus_thread {
color-scheme: none; color-scheme: none;
} }
} /* dark-scheme */
/* --- Syntax highlight theme from `rougify style base16.dark` --- */
.highlight .gp {
color: #87939d;
}
.highlight table td {
padding: 5px;
}
.highlight table pre {
margin: 0;
}
.highlight,
.highlight .w {
color: #d0d0d0;
background-color: #151515;
}
.highlight .err {
color: #151515;
background-color: #ac4142;
}
.highlight .c,
.highlight .ch,
.highlight .cd,
.highlight .cm,
.highlight .cpf,
.highlight .c1,
.highlight .cs {
color: #848484;
}
.highlight .cp {
color: #f4bf75;
}
.highlight .nt {
color: #f4bf75;
}
.highlight .o,
.highlight .ow {
color: #d0d0d0;
}
.highlight .p,
.highlight .pi {
color: #d0d0d0;
}
.highlight .gi {
color: #90a959;
}
.highlight .gd {
color: #f08a8b;
background-color: #320000;
}
.highlight .gh {
color: #6a9fb5;
background-color: #151515;
font-weight: bold;
}
.highlight .k,
.highlight .kn,
.highlight .kp,
.highlight .kr,
.highlight .kv {
color: #aa759f;
}
.highlight .kc {
color: #d28445;
}
.highlight .kt {
color: #d28445;
}
.highlight .kd {
color: #d28445;
}
.highlight .s,
.highlight .sb,
.highlight .sc,
.highlight .dl,
.highlight .sd,
.highlight .s2,
.highlight .sh,
.highlight .sx,
.highlight .s1 {
color: #90a959;
}
.highlight .sa {
color: #aa759f;
}
.highlight .sr {
color: #75b5aa;
}
.highlight .si {
color: #b76d45;
}
.highlight .se {
color: #b76d45;
}
.highlight .nn {
color: #f4bf75;
}
.highlight .nc {
color: #f4bf75;
}
.highlight .no {
color: #f4bf75;
}
.highlight .na {
color: #6a9fb5;
}
.highlight .m,
.highlight .mb,
.highlight .mf,
.highlight .mh,
.highlight .mi,
.highlight .il,
.highlight .mo,
.highlight .mx {
color: #90a959;
}
.highlight .ss {
color: #90a959;
}
}

313
_sass/themes/_light.scss Normal file
View file

@ -0,0 +1,313 @@
@mixin styles {
/* Framework color */
--main-bg: white;
--mask-bg: #c1c3c5;
--main-border-color: #f3f3f3;
/* Common color */
--text-color: #34343c;
--text-muted-color: #757575;
--text-muted-highlight-color: inherit;
--heading-color: #2a2a2a;
--label-color: #585858;
--blockquote-border-color: #eeeeee;
--blockquote-text-color: #757575;
--link-color: #0056b2;
--link-underline-color: #dee2e6;
--button-bg: #ffffff;
--btn-border-color: #e9ecef;
--btn-backtotop-color: #686868;
--btn-backtotop-border-color: #f1f1f1;
--checkbox-color: #c5c5c5;
--checkbox-checked-color: #07a8f7;
--img-bg: radial-gradient(
circle,
rgb(255, 255, 255) 0%,
rgb(239, 239, 239) 100%
);
--shimmer-bg: linear-gradient(
90deg,
rgba(250, 250, 250, 0) 0%,
rgba(232, 230, 230, 1) 50%,
rgba(250, 250, 250, 0) 100%
);
/* Sidebar */
--site-title-color: rgb(113, 113, 113);
--site-subtitle-color: #717171;
--sidebar-bg: #f6f8fa;
--sidebar-border-color: #efefef;
--sidebar-muted-color: #545454;
--sidebar-active-color: #1d1d1d;
--sidebar-hover-bg: rgb(223, 233, 241, 0.64);
--sidebar-btn-bg: white;
--sidebar-btn-color: #8e8e8e;
--avatar-border-color: white;
/* Topbar */
--topbar-bg: rgb(255, 255, 255, 0.7);
--topbar-text-color: rgb(78, 78, 78);
--search-border-color: rgb(240, 240, 240);
--search-icon-color: #c2c6cc;
--input-focus-border-color: #b8b8b8;
/* Home page */
--post-list-text-color: dimgray;
--btn-patinator-text-color: #555555;
--btn-paginator-hover-color: var(--sidebar-bg);
/* Posts */
--toc-highlight: #0550ae;
--toc-popup-border-color: lightgray;
--btn-share-color: gray;
--btn-share-hover-color: #0d6efd;
--card-bg: white;
--card-hovor-bg: #e2e2e2;
--card-shadow: rgb(104, 104, 104, 0.05) 0 2px 6px 0,
rgba(211, 209, 209, 0.15) 0 0 0 1px;
--footnote-target-bg: lightcyan;
--tb-odd-bg: #fbfcfd;
--tb-border-color: #eaeaea;
--dash-color: silver;
--kbd-wrap-color: #bdbdbd;
--kbd-text-color: var(--text-color);
--kbd-bg-color: white;
--prompt-text-color: rgb(46, 46, 46, 0.77);
--prompt-tip-bg: rgb(123, 247, 144, 0.2);
--prompt-tip-icon-color: #03b303;
--prompt-info-bg: #e1f5fe;
--prompt-info-icon-color: #0070cb;
--prompt-warning-bg: rgb(255, 243, 205);
--prompt-warning-icon-color: #ef9c03;
--prompt-danger-bg: rgb(248, 215, 218, 0.56);
--prompt-danger-icon-color: #df3c30;
/* Tags */
--tag-border: #dee2e6;
--tag-shadow: var(--btn-border-color);
--tag-hover: rgb(222, 226, 230);
--search-tag-bg: #f8f9fa;
/* Categories */
--categories-border: rgba(0, 0, 0, 0.125);
--categories-hover-bg: var(--btn-border-color);
--categories-icon-hover-color: darkslategray;
/* Archive */
--timeline-color: rgba(0, 0, 0, 0.075);
--timeline-node-bg: #c2c6cc;
--timeline-year-dot-color: #ffffff;
/* --- Custom code light mode colors --- */
--language-border-color: #ececec;
--highlight-bg-color: #f6f8fa;
--highlighter-rouge-color: #3f596f;
--highlight-lineno-color: #9e9e9e;
--inline-code-bg: rgba(25, 25, 28, 0.05);
--code-color: #3a3a3a;
--code-header-text-color: #a3a3a3;
--code-header-muted-color: #e5e5e5;
--code-header-icon-color: #c9c8c8;
--clipboard-checked-color: #43c743;
[class^='prompt-'] {
--link-underline-color: rgb(219, 216, 216);
}
.dark {
display: none;
}
/* --- Syntax highlight theme from `rougify style github` --- */
.highlight table td {
padding: 5px;
}
.highlight table pre {
margin: 0;
}
.highlight,
.highlight .w {
color: #24292f;
background-color: #f6f8fa;
}
.highlight .k,
.highlight .kd,
.highlight .kn,
.highlight .kp,
.highlight .kr,
.highlight .kt,
.highlight .kv {
color: #cf222e;
}
.highlight .gr {
color: #f6f8fa;
}
.highlight .gd {
color: #82071e;
background-color: #ffebe9;
}
.highlight .nb {
color: #953800;
}
.highlight .nc {
color: #953800;
}
.highlight .no {
color: #953800;
}
.highlight .nn {
color: #953800;
}
.highlight .sr {
color: #116329;
}
.highlight .na {
color: #116329;
}
.highlight .nt {
color: #116329;
}
.highlight .gi {
color: #116329;
background-color: #dafbe1;
}
.highlight .kc {
color: #0550ae;
}
.highlight .l,
.highlight .ld,
.highlight .m,
.highlight .mb,
.highlight .mf,
.highlight .mh,
.highlight .mi,
.highlight .il,
.highlight .mo,
.highlight .mx {
color: #0550ae;
}
.highlight .sb {
color: #0550ae;
}
.highlight .bp {
color: #0550ae;
}
.highlight .ne {
color: #0550ae;
}
.highlight .nl {
color: #0550ae;
}
.highlight .py {
color: #0550ae;
}
.highlight .nv,
.highlight .vc,
.highlight .vg,
.highlight .vi,
.highlight .vm {
color: #0550ae;
}
.highlight .o,
.highlight .ow {
color: #0550ae;
}
.highlight .gh {
color: #0550ae;
font-weight: bold;
}
.highlight .gu {
color: #0550ae;
font-weight: bold;
}
.highlight .s,
.highlight .sa,
.highlight .sc,
.highlight .dl,
.highlight .sd,
.highlight .s2,
.highlight .se,
.highlight .sh,
.highlight .sx,
.highlight .s1,
.highlight .ss {
color: #0a3069;
}
.highlight .nd {
color: #8250df;
}
.highlight .nf,
.highlight .fm {
color: #8250df;
}
.highlight .err {
color: #f6f8fa;
background-color: #82071e;
}
.highlight .c,
.highlight .ch,
.highlight .cd,
.highlight .cm,
.highlight .cp,
.highlight .cpf,
.highlight .c1,
.highlight .cs {
color: #68717a;
}
.highlight .gl {
color: #68717a;
}
.highlight .gt {
color: #68717a;
}
.highlight .ni {
color: #24292f;
}
.highlight .si {
color: #24292f;
}
.highlight .ge {
color: #24292f;
font-style: italic;
}
.highlight .gs {
color: #24292f;
font-weight: bold;
}
}

View file

@ -1,3 +0,0 @@
/*
Appending custom SCSS variables will override the default ones in `_sass/addon/variables.scsss`
*/

View file

@ -1,7 +1,7 @@
--- ---
--- ---
@import 'main @use 'main
{%- if jekyll.environment == 'production' -%} {%- if jekyll.environment == 'production' -%}
.bundle .bundle
{%- endif -%} {%- endif -%}

View file

@ -34,7 +34,7 @@ permalink: /feed.xml
<updated>{{ post.date | date_to_xmlschema }}</updated> <updated>{{ post.date | date_to_xmlschema }}</updated>
{% endif %} {% endif %}
<id>{{ post_absolute_url }}</id> <id>{{ post_absolute_url }}</id>
<content src="{{ post_absolute_url }}" /> <content type="text/html" src="{{ post_absolute_url }}" />
<author> <author>
<name>{{ post.author | default: site.social.name }}</name> <name>{{ post.author | default: site.social.name }}</name>
</author> </author>

@ -1 +1 @@
Subproject commit a231bc7e2c67198e604950cb2be9147a0b2020c0 Subproject commit b9e18a1510e3be5de250ed34205da318b76474e0

View file

@ -15,7 +15,7 @@
"homepage": "https://github.com/cotes2020/jekyll-theme-chirpy/", "homepage": "https://github.com/cotes2020/jekyll-theme-chirpy/",
"scripts": { "scripts": {
"build": "concurrently npm:build:*", "build": "concurrently npm:build:*",
"build:css": "purgecss -c purgecss.config.js", "build:css": "node purgecss.js",
"build:js": "rollup -c --bundleConfigAsCjs --environment BUILD:production", "build:js": "rollup -c --bundleConfigAsCjs --environment BUILD:production",
"watch:js": "rollup -c --bundleConfigAsCjs -w", "watch:js": "rollup -c --bundleConfigAsCjs -w",
"lint:scss": "stylelint _sass/**/*.scss", "lint:scss": "stylelint _sass/**/*.scss",
@ -28,24 +28,25 @@
"bootstrap": "^5.3.3" "bootstrap": "^5.3.3"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.25.2", "@babel/core": "^7.26.0",
"@babel/plugin-transform-class-properties": "^7.25.4", "@babel/plugin-transform-class-properties": "^7.25.9",
"@babel/preset-env": "^7.25.4", "@babel/plugin-transform-private-methods": "^7.25.9",
"@commitlint/cli": "^19.5.0", "@babel/preset-env": "^7.26.0",
"@commitlint/config-conventional": "^19.5.0", "@commitlint/cli": "^19.6.0",
"@commitlint/config-conventional": "^19.6.0",
"@rollup/plugin-babel": "^6.0.4", "@rollup/plugin-babel": "^6.0.4",
"@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-node-resolve": "^15.3.0",
"@rollup/plugin-terser": "^0.4.4", "@rollup/plugin-terser": "^0.4.4",
"@semantic-release/changelog": "^6.0.3", "@semantic-release/changelog": "^6.0.3",
"@semantic-release/exec": "^6.0.3", "@semantic-release/exec": "^6.0.3",
"@semantic-release/git": "^10.0.1", "@semantic-release/git": "^10.0.1",
"concurrently": "^9.0.1", "concurrently": "^9.1.0",
"conventional-changelog-conventionalcommits": "^8.0.0", "conventional-changelog-conventionalcommits": "^8.0.0",
"husky": "^9.1.6", "husky": "^9.1.7",
"purgecss": "^6.0.0", "purgecss": "^7.0.2",
"rollup": "^4.21.3", "rollup": "^4.27.4",
"semantic-release": "^24.1.1", "semantic-release": "^24.2.0",
"stylelint": "^16.9.0", "stylelint": "^16.10.0",
"stylelint-config-standard-scss": "^13.1.0" "stylelint-config-standard-scss": "^13.1.0"
}, },
"prettier": { "prettier": {
@ -67,41 +68,6 @@
] ]
} }
}, },
"stylelint": {
"extends": "stylelint-config-standard-scss",
"rules": {
"no-descending-specificity": null,
"shorthand-property-no-redundant-values": null,
"at-rule-no-vendor-prefix": null,
"property-no-vendor-prefix": null,
"selector-no-vendor-prefix": null,
"value-no-vendor-prefix": null,
"color-function-notation": "legacy",
"alpha-value-notation": "number",
"selector-not-notation": "simple",
"color-hex-length": "long",
"declaration-block-single-line-max-declarations": 3,
"scss/operator-no-newline-after": null,
"rule-empty-line-before": [
"always",
{
"ignore": [
"after-comment",
"first-nested"
]
}
],
"value-keyword-case": [
"lower",
{
"ignoreProperties": [
"/^\\$/"
]
}
],
"media-feature-range-notation": "prefix"
}
},
"release": { "release": {
"branches": [ "branches": [
"production" "production"

View file

@ -1,23 +0,0 @@
const fs = require('fs');
const DIST_PATH = '_sass/dist';
fs.rm(DIST_PATH, { recursive: true, force: true }, (err) => {
if (err) {
throw err;
}
fs.mkdirSync(DIST_PATH);
});
module.exports = {
content: ['_includes/**/*.html', '_layouts/**/*.html', '_javascript/**/*.js'],
css: ['node_modules/bootstrap/dist/css/bootstrap.min.css'],
keyframes: true,
variables: true,
output: `${DIST_PATH}/bootstrap.css`,
// The `safelist` should be changed appropriately for future development
safelist: {
standard: [/^collaps/, /^w-/, 'shadow', 'border', 'kbd'],
greedy: [/^col-/, /tooltip/]
}
};

30
purgecss.js Normal file
View file

@ -0,0 +1,30 @@
const fs = require('fs').promises;
const { PurgeCSS } = require('purgecss');
const DIST_PATH = '_sass/vendors';
const output = `${DIST_PATH}/_bootstrap.scss`;
const config = {
content: ['_includes/**/*.html', '_layouts/**/*.html', '_javascript/**/*.js'],
css: ['node_modules/bootstrap/dist/css/bootstrap.min.css'],
keyframes: true,
variables: true,
// The `safelist` should be changed appropriately for future development
safelist: {
standard: [/^collaps/, /^w-/, 'shadow', 'border', 'kbd'],
greedy: [/^col-/, /tooltip/]
}
};
function main() {
fs.rm(DIST_PATH, { recursive: true, force: true })
.then(() => fs.mkdir(DIST_PATH))
.then(() => new PurgeCSS().purge(config))
.then((result) => {
return fs.writeFile(output, result[0].css);
})
.catch((err) => {
console.error('Error during PurgeCSS process:', err);
});
}
main();

View file

@ -34,24 +34,32 @@ function insertFrontmatter() {
}; };
} }
function build(filename, { src = SRC_DEFAULT, jekyll = false } = {}) { function build(
filename,
{ src = SRC_DEFAULT, jekyll = false, outputName = null } = {}
) {
const input = `${src}/${filename}.js`;
return { return {
input: `${src}/${filename}.js`, input,
output: { output: {
file: `${DIST}/${filename}.min.js`, file: `${DIST}/${filename}.min.js`,
format: 'iife', format: 'iife',
name: 'Chirpy', ...(outputName !== null && { name: outputName }),
banner, banner,
sourcemap: !isProd && !jekyll sourcemap: !isProd && !jekyll
}, },
watch: { watch: {
include: `${src}/**` include: input
}, },
plugins: [ plugins: [
babel({ babel({
babelHelpers: 'bundled', babelHelpers: 'bundled',
presets: ['@babel/env'], presets: ['@babel/env'],
plugins: ['@babel/plugin-transform-class-properties'] plugins: [
'@babel/plugin-transform-class-properties',
'@babel/plugin-transform-private-methods'
]
}), }),
nodeResolve(), nodeResolve(),
isProd && terser(), isProd && terser(),
@ -69,6 +77,7 @@ export default [
build('page'), build('page'),
build('post'), build('post'),
build('misc'), build('misc'),
build('theme', { src: `${SRC_DEFAULT}/modules`, outputName: 'Theme' }),
build('app', { src: SRC_PWA, jekyll: true }), build('app', { src: SRC_PWA, jekyll: true }),
build('sw', { src: SRC_PWA, jekyll: true }) build('sw', { src: SRC_PWA, jekyll: true })
]; ];

View file

@ -92,7 +92,8 @@ init_files() {
npm i && npm run build npm i && npm run build
# track the CSS/JS output # track the CSS/JS output
_sedi "/.*\/dist$/d" .gitignore _sedi "/^_sass\/vendors/d" .gitignore
_sedi "/^assets\/js\/dist/d" .gitignore
} }
commit() { commit() {

View file

@ -15,7 +15,7 @@ NODE_SPEC="package.json"
CHANGELOG="docs/CHANGELOG.md" CHANGELOG="docs/CHANGELOG.md"
CONFIG="_config.yml" CONFIG="_config.yml"
CSS_DIST="_sass/dist" CSS_DIST="_sass/vendors"
JS_DIST="assets/js/dist" JS_DIST="assets/js/dist"
FILES=( FILES=(