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:
push:
branches:
- production
tags-ignore:
- "**"
branches: [production]
tags-ignore: ["**"]
jobs:
release:
if: ${{ ! startsWith(github.event.head_commit.message, 'chore(release)') }}
permissions:
contents: write
issues: write

View file

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

View file

@ -1,5 +1,11 @@
name: Lint Commit Messages
on: pull_request
on:
push:
branches:
- master
- "hotfix/*"
pull_request:
jobs:
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
BUILDER:
required: true
workflow_dispatch:
jobs:
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
env:
STALE_LABEL: stale
STALE_LABEL: inactive
EXEMPT_LABELS: "pending,planning,in progress"
MESSAGE: >
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
# Misc
_sass/dist
_sass/vendors
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"
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
tags: Címkék
archives: Archívum
about: Rólam
about: Bemutatkozás
# the text displayed in the search bar & search results
search:
hint: keresés
cancel: Mégse
no_results: Oops! Nincs találat a keresésre.
no_results: Hoppá! Nincs találat a keresésre.
panel:
lastmod: Legutóbb frissítve
trending_tags: Népszerű Címkék
toc: Tartalom
links: Blog linkek
copyright:
# Shown at the bottom of the post
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
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,
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:
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
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:
category_measure: kategória
post_measure: bejegyzés
category_measure:
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
toc:
css: https://cdn.jsdelivr.net/npm/tocbot@4.29.0/dist/tocbot.min.css
js: https://cdn.jsdelivr.net/npm/tocbot@4.29.0/dist/tocbot.min.js
css: https://cdn.jsdelivr.net/npm/tocbot@4.32.2/dist/tocbot.min.css
js: https://cdn.jsdelivr.net/npm/tocbot@4.32.2/dist/tocbot.min.js
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:
js: https://cdn.jsdelivr.net/npm/simple-jekyll-search@1.10.0/dest/simple-jekyll-search.min.js
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:
js:

View file

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

View file

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

View file

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

View file

@ -1,14 +1,13 @@
<!-- Matomo -->
<script type="text/javascript">
var _paq = window._paq = window._paq || [];
<script>
document.addEventListener('DOMContentLoaded', () => {
var _paq = (window._paq = window._paq || []);
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function() {
var u="//{{ site.analytics.matomo.domain }}/";
_paq.push(['setTrackerUrl', u+'matomo.php']);
_paq.push(['setSiteId', {{ site.analytics.matomo.id }}]);
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);
})();
});
</script>
<!-- End Matomo Code -->

View file

@ -1,38 +1,25 @@
<!-- The Disqus lazy loading. -->
<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">
<script>
var disqus_config = function () {
this.page.url = '{{ page.url | absolute_url }}';
this.page.identifier = '{{ page.url }}';
};
{%- comment -%} Lazy loading {%- endcomment -%}
var disqus_observer = new IntersectionObserver(
function (entries) {
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);
})();
function addDisqus() {
let disqusThread = document.createElement('div');
let paragraph = document.createElement('p');
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>.';
disqusThread.appendChild(paragraph);
const footer = document.querySelector('footer');
footer.insertAdjacentElement("beforebegin", disqusThread);
}
},
{ threshold: [0] }
);
disqus_observer.observe(document.getElementById('disqus_thread'));
{%- comment -%} Auto switch theme {%- endcomment -%}
function reloadDisqus() {
if (event.source === window && event.data && event.data.direction === ModeToggle.ID) {
function reloadDisqus(event) {
if (event.source === window && event.data && event.data.id === Theme.ID) {
{%- comment -%} Disqus hasn't been loaded {%- endcomment -%}
if (typeof DISQUS === 'undefined') {
return;
@ -44,7 +31,27 @@
}
}
if (document.getElementById('mode-toggle')) {
window.addEventListener('message', reloadDisqus);
addDisqus();
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>

View file

@ -1,21 +1,8 @@
<!-- https://giscus.app/ -->
<script type="text/javascript">
<script>
(function () {
const origin = 'https://giscus.app';
const lightTheme = 'light';
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;
}
const themeMapper = Theme.getThemeMapper('light', 'dark_dimmed');
const initTheme = themeMapper[Theme.visualState];
let lang = '{{ site.comments.giscus.lang | default: lang }}';
{%- comment -%} https://github.com/giscus/giscus/tree/main/locales {%- endcomment -%}
@ -41,30 +28,27 @@
async: ''
};
let giscusScript = document.createElement('script');
let giscusNode = document.createElement('script');
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) => {
if (
event.source === window &&
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;
if (event.source === window && event.data && event.data.id === Theme.ID) {
const newTheme = themeMapper[Theme.visualState];
const message = {
setConfig: {
theme: theme
theme: newTheme
}
};
const giscus = document.getElementsByClassName('giscus-frame')[0].contentWindow;
giscus.postMessage({ giscus: message }, origin);
const giscus =
document.getElementsByClassName('giscus-frame')[0].contentWindow;
giscus.postMessage({ giscus: message }, 'https://giscus.app');
}
});
})();

View file

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

View file

@ -70,7 +70,7 @@
<!-- Bootstrap -->
{% 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 %}
<!-- Theme style -->
@ -97,11 +97,32 @@
<link rel="stylesheet" href="{{ site.data.origin[type].glightbox.css | relative_url }}">
{% endif %}
<!-- JavaScript -->
<!-- Scripts -->
{% unless site.theme_mode %}
{% include mode-toggle.html %}
<script src="{{ '/assets/js/dist/theme.min.js' | relative_url }}"></script>
{% 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 %}
</head>

View file

@ -62,12 +62,12 @@
{% 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 %}
<!-- MathJax -->
<script 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="{{ '/assets/js/data/mathjax.js' | relative_url }}"></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>
{% endif %}
@ -84,26 +84,3 @@
{% endcase %}
{% 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 combined_urls = nil %}
{% assign combined_urls = null %}
{% assign domain = 'https://cdn.jsdelivr.net/' %}
@ -15,12 +15,12 @@
{% endif %}
{% elsif url contains '//' %}
<script src="{{ url }}"></script>
<script defer src="{{ url }}"></script>
{% else %}
<script src="{{ url | relative_url }}"></script>
<script defer src="{{ url | relative_url }}"></script>
{% endif %}
{% endfor %}
{% if combined_urls %}
<script src="{{ combined_urls }}"></script>
<script defer src="{{ combined_urls }}"></script>
{% 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,7 +19,8 @@
{% capture not_found %}<p class="mt-5">{{ site.data.locales[include.lang].search.no_results }}</p>{% endcapture %}
<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 %}
document.addEventListener('DOMContentLoaded', () => {
SimpleJekyllSearch({
searchInput: document.getElementById('search-input'),
resultsContainer: document.getElementById('search-results'),
@ -44,4 +45,5 @@
}
}
});
});
</script>

View file

@ -11,9 +11,7 @@
{%- endif -%}
</a>
<h1 class="site-title">
<a href="{{ '/' | relative_url }}">{{ site.title }}</a>
</h1>
<a class="site-title d-block" href="{{ '/' | relative_url }}">{{ site.title }}</a>
<p class="site-subtitle fst-italic mb-0">{{ site.tagline }}</p>
</header>
<!-- .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 %}
{% if site.toc and page.toc %}
{% if page.content contains '<h2' or page.content contains '<h3' %}
{% assign enable_toc = true %}
{% endif %}
{% endif %}
{% include toc-status.html %}
{% if enable_toc %}
<section id="toc-wrapper" class="d-none ps-0 pe-4">
<h2 class="panel-heading ps-3 mb-2">{{- site.data.locales[include.lang].panel.toc -}}</h2>
<div class="toc-border-cover z-3"></div>
<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>
</section>
{% endif %}

View file

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

View file

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

View file

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

View file

@ -3,4 +3,8 @@ export { initClipboard } from './components/clipboard';
export { loadImg } from './components/img-loading';
export { imgPopup } from './components/img-popup';
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
*/
const html = document.documentElement;
const lightImages = '.popup:not(.dark)';
const darkImages = '.popup:not(.light)';
let selector = lightImages;
@ -33,26 +32,17 @@ export function imgPopup() {
document.querySelector('.popup.dark') === null
);
if (
(html.hasAttribute('data-mode') &&
html.getAttribute('data-mode') === 'dark') ||
(!html.hasAttribute('data-mode') &&
window.matchMedia('(prefers-color-scheme: dark)').matches)
) {
if (Theme.visualState === Theme.DARK) {
selector = darkImages;
}
let current = GLightbox({ selector: `${selector}` });
if (hasDualImages && document.getElementById('mode-toggle')) {
if (hasDualImages && Theme.switchable) {
let reverse = null;
window.addEventListener('message', (event) => {
if (
event.source === window &&
event.data &&
event.data.direction === ModeToggle.ID
) {
if (event.source === window && event.data && event.data.id === Theme.ID) {
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() {
if (document.querySelector('main h2, main h3')) {
// 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
});
import { TocMobile as mobile } from './toc/toc-mobile';
import { TocDesktop as desktop } from './toc/toc-desktop';
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 { loadTooptip } from '../components/tooltip-loader';
import { back2top, loadTooptip, modeWatcher } from '../components';
export function basic() {
modeWatcher();
back2top();
loadTooptip();
}

View file

@ -1,7 +1,19 @@
import { modeWatcher } from '../components/mode-watcher';
import { sidebarExpand } from '../components/sidebar';
const ATTR_DISPLAY = 'sidebar-display';
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() {
modeWatcher();
sidebarExpand();
$trigger.onclick = $mask.onclick = () => SidebarUtil.toggle();
}

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 { loadImg, imgPopup, initClipboard } from './modules/plugins';
import {
loadImg,
imgPopup,
initClipboard,
loadMermaid
} from './modules/components';
loadImg();
imgPopup();
initSidebar();
initTopbar();
initClipboard();
loadMermaid();
basic();

View file

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

View file

@ -33,7 +33,7 @@ layout: compress
</main>
<!-- 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">
{% include_cached update-list.html lang=lang %}
{% include_cached trending-tags.html lang=lang %}
@ -68,14 +68,18 @@ layout: compress
</aside>
</div>
<div id="mask"></div>
<div id="mask" class="d-none position-fixed w-100 h-100 z-1"></div>
{% if site.pwa.enabled %}
{% include_cached notification.html lang=lang %}
{% endif %}
<!-- JavaScripts -->
{% include js-selector.html lang=lang %}
<!-- Embedded scripts -->
{% for _include in layout.script_includes %}
{% assign _include_path = _include | append: '.html' %}
{% include {{ _include_path }} %}
{% endfor %}
{% include_cached search-loader.html lang=lang %}
</body>

View file

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

View file

@ -6,12 +6,15 @@ panel_includes:
tail_includes:
- related-posts
- post-nav
- comments
script_includes:
- comment
---
{% include lang.html %}
<article class="px-1">
{% include toc-status.html %}
<article class="px-1" data-toc="{{ enable_toc }}">
<header>
<h1 data-toc-skip>{{ page.title }}</h1>
{% if page.description %}
@ -95,6 +98,30 @@ tail_includes:
</div>
</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">
{{ content }}
</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.
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
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 @@
/*
* Mainly scss modules, only imported to `assets/css/main.scss`
*/
/* ---------- scss placeholder --------- */
@use 'variables' as v;
@use 'mixins' as mx;
%heading {
color: var(--heading-color);
font-weight: 400;
font-family: $font-family-heading;
font-family: v.$font-family-heading;
scroll-margin-top: 3.5rem;
}
%anchor {
@ -81,7 +79,7 @@
}
%rounded {
border-radius: $radius-lg;
border-radius: v.$radius-lg;
}
%img-caption {
@ -111,6 +109,10 @@
-webkit-box-orient: vertical;
}
%text-ellipsis {
@include mx.text-ellipsis;
}
%text-highlight {
color: var(--text-muted-highlight-color);
font-weight: 600;
@ -134,60 +136,25 @@
}
}
/* ---------- scss mixin --------- */
@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) {
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);
}
}
%btn-color {
button i {
color: #999999;
}
}
%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-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 @@
/*
* The syntax highlight.
*/
@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;
}
@use '../abstracts/variables' as v;
@use '../abstracts/breakpoints' as bp;
@use '../abstracts/mixins' as mx;
@use '../abstracts/placeholders';
.highlighter-rouge {
color: var(--highlighter-rouge-color);
@ -59,7 +22,7 @@ html {
pre {
margin-bottom: 0;
font-size: $code-font-size;
font-size: v.$code-font-size;
line-height: 1.4rem;
word-wrap: normal; /* Fixed Safari overflow-x */
}
@ -101,10 +64,10 @@ code {
color: var(--code-color);
&.highlighter-rouge {
font-size: $code-font-size;
font-size: v.$code-font-size;
padding: 3px 5px;
word-break: break-word;
border-radius: $radius-sm;
border-radius: v.$radius-sm;
background-color: var(--inline-code-bg);
}
@ -150,9 +113,42 @@ div[class^='language-'] {
box-shadow: var(--language-border-color) 0 0 0 1px;
.content > & {
@include ml-mr(-1rem);
@include mx.ml-mr(-1rem);
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 {
@ -184,18 +180,18 @@ div {
display: flex;
justify-content: space-between;
align-items: center;
height: $code-header-height;
height: v.$code-header-height;
margin-left: 0.75rem;
margin-right: 0.25rem;
/* the label block */
span {
line-height: $code-header-height;
line-height: v.$code-header-height;
/* label icon */
i {
font-size: 1rem;
width: $code-icon-width;
width: v.$code-icon-width;
color: var(--code-header-icon-color);
&.small {
@ -223,8 +219,8 @@ div {
@extend %rounded;
border: 1px solid transparent;
height: $code-header-height;
width: $code-header-height;
height: v.$code-header-height;
width: v.$code-header-height;
padding: 0;
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';
@import 'main';
@use 'vendors/bootstrap';
@use 'main';

View file

@ -1,13 +1,4 @@
@import 'colors/typography-light';
@import 'colors/typography-dark';
@import 'addon/variables';
@import 'variables-hook';
@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';
@forward 'base';
@forward 'components';
@forward 'layout';
@forward 'pages';

View file

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

View file

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

View file

@ -1,6 +1,6 @@
/*
Style for page Category and Tag
*/
@use '../abstracts/breakpoints' as bp;
@use '../abstracts/mixins' as mx;
@use '../abstracts/placeholders';
.dash {
margin: 0 0.5rem 0.6rem 0.5rem;
@ -24,6 +24,10 @@
position: relative;
top: 0.6rem;
margin-right: 0.5rem;
@include bp.lt(bp.get(sm)) {
margin: 0 0.5rem;
}
}
/* post's title */
@ -31,6 +35,10 @@
@extend %no-bottom-border;
font-size: 1.1rem;
@include bp.lt(bp.get(sm)) {
@include mx.text-ellipsis;
}
}
}
}
@ -53,20 +61,3 @@
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 @@
/*
Style for Homepage
*/
@use '../abstracts/variables' as v;
@use '../abstracts/breakpoints' as bp;
@use '../abstracts/placeholders';
#post-list {
margin-top: 2rem;
@include bp.lg {
margin-top: 2.5rem;
}
.card-wrapper {
&:hover {
text-decoration: none;
@ -20,7 +24,11 @@
background: none;
%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 {
@ -35,6 +43,10 @@
height: 100%;
padding: 1rem;
@include bp.md {
padding: 1.75rem 1.75rem 1.25rem;
}
.card-title {
@extend %text-clip;
@ -46,7 +58,12 @@
color: var(--text-muted-color) !important;
}
.card-text.content {
.card-text {
@include bp.md {
display: inherit !important;
}
&.content {
@extend %muted;
p {
@ -56,6 +73,7 @@
margin: 0;
}
}
}
.post-meta {
@extend %muted;
@ -63,6 +81,10 @@
i {
&:not(:first-child) {
margin-left: 1.5rem;
@include bp.md {
margin-left: 1.75rem;
}
}
}
@ -74,9 +96,8 @@
> div:first-child {
display: block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
@extend %text-ellipsis;
}
}
}
@ -88,11 +109,28 @@
font-family: Lato, sans-serif;
justify-content: space-evenly;
@include bp.lg {
font-size: 0.85rem;
justify-content: center;
}
a:hover {
text-decoration: none;
}
.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 {
color: var(--btn-patinator-text-color);
padding: 0 0.6rem;
@ -126,64 +164,10 @@
}
}
} /* .page-item */
} /* .pagination */
/* Tablet */
@media all and (min-width: 768px) {
%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 {
@include bp.lg {
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 @@
/*
Post-specific style
*/
@use '../abstracts/variables' as v;
@use '../abstracts/breakpoints' as bp;
@use '../abstracts/mixins' as mx;
@use '../abstracts/placeholders';
%btn-post-nav {
%-btn-post-nav {
width: 50%;
position: relative;
border-color: var(--btn-border-color);
}
@mixin dot($pl: 0.25rem, $pr: 0.25rem) {
@mixin -dot($pl: 0.25rem, $pr: 0.25rem) {
content: '\2022';
padding-left: $pl;
padding-right: $pr;
@ -24,7 +25,7 @@ header {
.post-meta {
span + span::before {
@include dot;
@include -dot;
}
em,
@ -97,7 +98,7 @@ header {
&:hover {
i {
@extend %btn-share-hovor;
@extend %btn-share-hover;
}
}
}
@ -113,9 +114,20 @@ header {
} /* .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 {
/* 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-form-button-border: 1px solid var(--btn-border-color);
--wc-stm-form-submit-background-color: var(--sidebar-btn-bg);
@ -139,8 +151,13 @@ header {
}
.post-navigation {
@include bp.lt(bp.get(lg)) {
@include mx.pl-pr(0);
@include mx.ml-mr(-0.5rem);
}
.btn {
@extend %btn-post-nav;
@extend %-btn-post-nav;
&:not(:hover) {
color: var(--link-color);
@ -153,7 +170,7 @@ header {
}
&.disabled {
@extend %btn-post-nav;
@extend %-btn-post-nav;
pointer-events: auto;
cursor: not-allowed;
@ -173,12 +190,12 @@ header {
}
&:first-child {
border-radius: $radius-lg 0 0 $radius-lg;
border-radius: v.$radius-lg 0 0 v.$radius-lg;
left: 0.5px;
}
&:last-child {
border-radius: 0 $radius-lg $radius-lg 0;
border-radius: 0 v.$radius-lg v.$radius-lg 0;
right: 0.5px;
}
}
@ -228,14 +245,27 @@ header {
}
}
#toc-wrapper {
border-left: 1px solid rgba(158, 158, 158, 0.17);
position: -webkit-sticky;
/* TOC panel */
%top-cover {
content: '';
display: block;
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;
-webkit-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 {
list-style: none;
@ -248,6 +278,10 @@ header {
margin: 0.4rem 0;
}
&:first-child {
margin-top: 0;
}
a {
padding: 0.2rem 0 0.2rem 1.25rem;
}
@ -257,9 +291,8 @@ header {
.toc-link {
display: block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
@extend %text-ellipsis;
&:hover {
color: var(--toc-highlight);
@ -278,7 +311,6 @@ header {
&::before {
display: inline-block;
width: 1px;
left: -1px;
height: 1.25rem;
background-color: var(--toc-highlight) !important;
}
@ -288,13 +320,130 @@ header {
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 {
> h3 {
@include label(1.1rem, 600);
@include mx.label(1.1rem, 600);
}
time {
@ -305,10 +454,11 @@ header {
}
p {
@extend %text-ellipsis;
font-size: 0.9rem;
margin-bottom: 0.5rem;
overflow: hidden;
text-overflow: ellipsis;
white-space: break-spaces;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
@ -328,43 +478,31 @@ header {
.utterances {
max-width: 100%;
min-height: 269px;
}
%btn-share-hovor {
%btn-share-hover {
color: var(--btn-share-hover-color) !important;
}
.share-label {
@include label(inherit, 400, inherit);
@include mx.label(inherit, 400, inherit);
&::after {
content: ':';
}
}
@media all and (max-width: 576px) {
.post-tail-bottom {
flex-wrap: wrap-reverse !important;
> div:first-child {
width: 100%;
margin-top: 1rem;
}
}
}
@media all and (max-width: 768px) {
.content > p > img {
.content > p > img {
@include bp.lte(bp.get(md)) {
max-width: calc(100% + 1rem);
}
}
/* Hide SideBar and TOC */
@media all and (max-width: 849px) {
.post-navigation {
padding-left: 0;
padding-right: 0;
margin-left: -0.5rem;
margin-right: -0.5rem;
h2,
h3,
h4 {
@include bp.xl {
scroll-margin-top: 2rem;
}
}

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

View file

@ -1,8 +1,6 @@
/*
* The main dark mode styles
*/
@mixin styles {
color-scheme: dark;
@mixin dark-scheme {
/* Framework color */
--main-bg: rgb(27, 27, 30);
--mask-bg: rgb(68, 69, 70);
@ -22,7 +20,6 @@
--btn-border-color: #2e2f31;
--btn-backtotop-color: var(--text-color);
--btn-backtotop-border-color: #212122;
--btn-box-shadow: var(--main-bg);
--card-header-bg: #292929;
--checkbox-color: rgb(118, 120, 121);
--checkbox-checked-color: var(--link-color);
@ -60,6 +57,7 @@
/* Posts */
--toc-highlight: rgb(116, 178, 243);
--toc-popup-border-color: #373737;
--tag-hover: rgb(43, 56, 62);
--tb-odd-bg: #252526; /* odd 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-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 {
display: none;
@ -144,4 +153,151 @@
#disqus_thread {
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' -%}
.bundle
{%- endif -%}

View file

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

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

View file

@ -15,7 +15,7 @@
"homepage": "https://github.com/cotes2020/jekyll-theme-chirpy/",
"scripts": {
"build": "concurrently npm:build:*",
"build:css": "purgecss -c purgecss.config.js",
"build:css": "node purgecss.js",
"build:js": "rollup -c --bundleConfigAsCjs --environment BUILD:production",
"watch:js": "rollup -c --bundleConfigAsCjs -w",
"lint:scss": "stylelint _sass/**/*.scss",
@ -28,24 +28,25 @@
"bootstrap": "^5.3.3"
},
"devDependencies": {
"@babel/core": "^7.25.2",
"@babel/plugin-transform-class-properties": "^7.25.4",
"@babel/preset-env": "^7.25.4",
"@commitlint/cli": "^19.5.0",
"@commitlint/config-conventional": "^19.5.0",
"@babel/core": "^7.26.0",
"@babel/plugin-transform-class-properties": "^7.25.9",
"@babel/plugin-transform-private-methods": "^7.25.9",
"@babel/preset-env": "^7.26.0",
"@commitlint/cli": "^19.6.0",
"@commitlint/config-conventional": "^19.6.0",
"@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",
"@semantic-release/changelog": "^6.0.3",
"@semantic-release/exec": "^6.0.3",
"@semantic-release/git": "^10.0.1",
"concurrently": "^9.0.1",
"concurrently": "^9.1.0",
"conventional-changelog-conventionalcommits": "^8.0.0",
"husky": "^9.1.6",
"purgecss": "^6.0.0",
"rollup": "^4.21.3",
"semantic-release": "^24.1.1",
"stylelint": "^16.9.0",
"husky": "^9.1.7",
"purgecss": "^7.0.2",
"rollup": "^4.27.4",
"semantic-release": "^24.2.0",
"stylelint": "^16.10.0",
"stylelint-config-standard-scss": "^13.1.0"
},
"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": {
"branches": [
"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 {
input: `${src}/${filename}.js`,
input,
output: {
file: `${DIST}/${filename}.min.js`,
format: 'iife',
name: 'Chirpy',
...(outputName !== null && { name: outputName }),
banner,
sourcemap: !isProd && !jekyll
},
watch: {
include: `${src}/**`
include: input
},
plugins: [
babel({
babelHelpers: 'bundled',
presets: ['@babel/env'],
plugins: ['@babel/plugin-transform-class-properties']
plugins: [
'@babel/plugin-transform-class-properties',
'@babel/plugin-transform-private-methods'
]
}),
nodeResolve(),
isProd && terser(),
@ -69,6 +77,7 @@ export default [
build('page'),
build('post'),
build('misc'),
build('theme', { src: `${SRC_DEFAULT}/modules`, outputName: 'Theme' }),
build('app', { 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
# track the CSS/JS output
_sedi "/.*\/dist$/d" .gitignore
_sedi "/^_sass\/vendors/d" .gitignore
_sedi "/^assets\/js\/dist/d" .gitignore
}
commit() {

View file

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