1
0
mirror of https://github.com/cotes2020/jekyll-theme-chirpy.git synced 2025-12-18 21:53:26 +00:00

Compare commits

...

20 Commits

Author SHA1 Message Date
Andrew
9706811c3a Merge a4812e4a1f into 65f960c31a 2024-11-21 07:02:30 -05:00
Andrew
a4812e4a1f Merge branch 'master' into bugfix-PR 2024-11-21 07:02:26 -05:00
Cotes Chung
65f960c31a perf: speed up page rendering and jekyll build process (#2034)
Some checks failed
Close stale issues and PRs / stale (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Style Lint / stylelint (push) Has been cancelled
Lint Commit Messages / commitlint (push) Has been cancelled
Build and Deploy / build (push) Has been cancelled
Build and Deploy / deploy (push) Has been cancelled
- Ensure inline scripts execute after the DOM has fully loaded.
- Use Rollup to bundle the theme-mode and Mermaid scripts, reducing the number of Jekyll include snippets.
2024-11-16 22:49:55 +08:00
Cotes Chung
d51345e297 ci: reduce unnecessary pr-filter runs (#2033)
- Checking the repository of the PR is more effective than checking the label to identify bot-initiated PRs
- This change also allows more flexible PR body definitions for developers with write access to the repository
2024-11-08 22:35:18 +08:00
Cotes Chung
2f844978aa chore: change stale label to inactive 2024-11-08 22:15:31 +08:00
Supreeth Mysore Venkatesh
42dea8ee29 build(deps): update wdm gem version for compatibility (#2028) 2024-11-04 00:45:59 +08:00
classicrocker883
81d3d055e4 fix: mitigate Dart Sass depreciation warnings 2024-11-02 05:05:02 -04:00
Alexander Fuks
86b13c917f chore: improve feed interoperability (#2024) 2024-11-01 14:39:03 +04:00
Cotes Chung
4ef3cd8efc ci: improve workflow triggers (#2017)
- Unchain commit-lint and CI
  - Even if a commit does not meet the CI path filter, it still needs to lint the commit message.
- Unchain PR filter and CI
  - The CI workflow needs to be triggered when the commits in a pull request are modified.
- Allow manual publishing
  - Sometimes `semantic-release` will error out due to commit messages referencing discussions, but this does not affect the final RubyGems/GitHub Release. In such cases, manual triggering of the publish process is needed to complete the remaining publishing steps.
2024-10-29 22:56:32 +08:00
Cotes Chung
c7f967529c ci: skip test for invalid PRs (#2013) 2024-10-26 16:58:07 +08:00
Cotes Chung
74ed06321c ci: block invalid pull requests (#2010) 2024-10-25 19:48:11 +08:00
Cotes Chung
d4f7f39ece refactor: simplify sidebar animation 2024-10-22 11:13:06 +08:00
Cotes Chung
c1bd9eb9ee refactor: reduce duplicate scss 2024-10-20 13:52:42 +08:00
Cotes Chung
6f461132c0 refactor: improve toc popup module 2024-10-19 21:15:31 +08:00
Alexander Fuks
03e302cbf6 chore: close toc-popup gracefully with Esc key (#1990) 2024-10-14 01:16:25 +08:00
Alexander Fuks
8a064a5e5a feat: show toc on mobile screens (#1964) 2024-10-11 22:32:10 +08:00
Bence Boros
740bd84c51 chore(i18n): update hungarian translation (#1976) 2024-10-01 23:19:39 +08:00
Cotes Chung
93f616b25d fix: pagination error when pinned posts exceed the page size (#1965) 2024-09-26 22:30:56 +08:00
Alexander Fuks
e6b87d2811 chore: remove h1 element from site title (#1960) 2024-09-24 08:21:34 +08:00
semantic-release-bot
73749067c5 Merge branch 'production' 2024-09-23 11:56:46 +00:00
63 changed files with 1263 additions and 738 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,8 +1,10 @@
name: "Style Lint"
name: Style Lint
on:
push:
branches: ["master", "hotfix/**"]
branches:
- master
- "hotfix/*"
paths: ["_sass/**/*.scss"]
pull_request:
paths: ["_sass/**/*.scss"]

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: Hop! 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

@@ -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 || [];
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function() {
<script>
document.addEventListener('DOMContentLoaded', () => {
var _paq = (window._paq = window._paq || []);
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
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();
}
},
{ threshold: [0] }
);
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);
disqus_observer.observe(document.getElementById('disqus_thread'));
const footer = document.querySelector('footer');
footer.insertAdjacentElement("beforebegin", disqusThread);
}
{%- 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: newTheme
};
const utterances = document.querySelector('.utterances-frame').contentWindow;
utterances.postMessage(message, origin);
}
const message = {
type: 'set-theme',
theme: theme
};
const utterances = document.getElementsByClassName('utterances-frame')[0].contentWindow;
utterances.postMessage(message, origin);
});
})();
</script>

View File

@@ -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,29 +19,31 @@
{% 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 -%}
SimpleJekyllSearch({
searchInput: document.getElementById('search-input'),
resultsContainer: document.getElementById('search-results'),
json: '{{ '/assets/js/data/search.json' | relative_url }}',
searchResultTemplate: '{{ result_elem | strip_newlines }}',
noResultsText: '{{ not_found }}',
templateMiddleware: function(prop, value, template) {
if (prop === 'categories') {
if (value === '') {
return `${value}`;
} else {
return `<div class="me-sm-4"><i class="far fa-folder fa-fw"></i>${value}</div>`;
{% 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'),
json: '{{ '/assets/js/data/search.json' | relative_url }}',
searchResultTemplate: '{{ result_elem | strip_newlines }}',
noResultsText: '{{ not_found }}',
templateMiddleware: function(prop, value, template) {
if (prop === 'categories') {
if (value === '') {
return `${value}`;
} else {
return `<div class="me-sm-4"><i class="far fa-folder fa-fw"></i>${value}</div>`;
}
}
}
if (prop === 'tags') {
if (value === '') {
return `${value}`;
} else {
return `<div><i class="fa fa-tag fa-fw"></i>${value}</div>`;
if (prop === 'tags') {
if (value === '') {
return `${value}`;
} else {
return `<div><i class="fa fa-tag fa-fw"></i>${value}</div>`;
}
}
}
}
});
});
</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,12 +1,7 @@
{% 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">
<section id="toc-wrapper" class="ps-0 pe-4">
<h2 class="panel-heading ps-3 mb-2">{{- site.data.locales[include.lang].panel.toc -}}</h2>
<nav id="toc"></nav>
</section>

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

@@ -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

@@ -1,3 +1,8 @@
@use '../colors/typography-dark';
@use '../colors/typography-light';
@use 'module';
@use 'variables';
/* The common styles */
html {
@@ -6,22 +11,22 @@ html {
@media (prefers-color-scheme: light) {
&:not([data-mode]),
&[data-mode='light'] {
@include light-scheme;
@include typography-light.light-scheme;
}
&[data-mode='dark'] {
@include dark-scheme;
@include typography-dark.dark-scheme;
}
}
@media (prefers-color-scheme: dark) {
&:not([data-mode]),
&[data-mode='dark'] {
@include dark-scheme;
@include typography-dark.dark-scheme;
}
&[data-mode='light'] {
@include light-scheme;
@include typography-light.light-scheme;
}
}
}
@@ -32,17 +37,17 @@ body {
env(safe-area-inset-bottom) env(safe-area-inset-left);
color: var(--text-color);
-webkit-font-smoothing: antialiased;
font-family: $font-family-base;
font-family: variables.$font-family-base;
}
/* --- Typography --- */
@for $i from 1 through 5 {
h#{$i} {
@extend %heading;
@extend %heading !optional;
@if $i > 1 {
@extend %anchor;
@extend %anchor !optional;
}
@if $i < 5 {
@@ -70,7 +75,7 @@ body {
}
a {
@extend %link-color;
@extend %link-color !optional;
text-decoration: none;
}
@@ -104,7 +109,7 @@ blockquote {
padding: 1rem 1rem 1rem 3rem;
color: var(--prompt-text-color);
@extend %rounded;
@extend %rounded !optional;
&::before {
text-align: center;
@@ -117,10 +122,10 @@ blockquote {
}
}
@include prompt('tip', '\f0eb', $fa-style: 'regular');
@include prompt('info', '\f06a', $rotate: 180);
@include prompt('warning', '\f06a');
@include prompt('danger', '\f071');
@include module.prompt('tip', '\f0eb', $fa-style: 'regular');
@include module.prompt('info', '\f06a', $rotate: 180);
@include module.prompt('warning', '\f06a');
@include module.prompt('danger', '\f071');
}
kbd {
@@ -134,7 +139,7 @@ kbd {
padding-top: 0.1rem;
color: var(--kbd-text-color);
background-color: var(--kbd-bg-color);
border-radius: $radius-sm;
border-radius: variables.$radius-sm;
border: solid 1px var(--kbd-wrap-color);
box-shadow: inset 0 -2px 0 var(--kbd-wrap-color);
}
@@ -146,21 +151,21 @@ hr {
footer {
background-color: var(--main-bg);
height: $footer-height;
height: variables.$footer-height;
border-top: 1px solid var(--main-border-color);
@extend %text-xs;
@extend %text-xs !optional;
a {
@extend %text-highlight;
@extend %text-highlight !optional;
&:hover {
@extend %link-hover;
@extend %link-hover !optional;
}
}
em {
@extend %text-highlight;
@extend %text-highlight !optional;
}
p {
@@ -173,7 +178,7 @@ footer {
i {
&.far,
&.fas {
@extend %no-cursor;
@extend %no-cursor !optional;
}
}
@@ -210,7 +215,7 @@ i {
font-family: inherit;
line-height: inherit;
@include label(inherit);
@include module.label(inherit);
}
.post-tag {
@@ -231,10 +236,10 @@ i {
color: inherit;
&:hover {
@extend %link-hover;
@extend %link-hover !optional;
}
@extend %no-bottom-border;
@extend %no-bottom-border !optional;
}
}
@@ -247,27 +252,27 @@ i {
margin-bottom: 0.3rem;
}
@extend %sup-fn-target;
@extend %sup-fn-target !optional;
> p {
margin-left: 0.25em;
margin-top: 0;
margin-bottom: 0;
@include module.mt-mb(0);
}
}
}
.footnote {
@at-root a#{&} {
@include ml-mr(1px);
@include pl-pr(2px);
@include module.ml-mr(1px);
@include module.pl-pr(2px);
border-bottom-style: none !important;
}
}
sup {
@extend %sup-fn-target;
@extend %sup-fn-target !optional;
}
.reversefootnote {
@@ -297,7 +302,7 @@ sup {
border-bottom: solid 2px rgba(210, 215, 217, 0.75);
th {
@extend %table-cell;
@extend %table-cell !optional;
}
}
@@ -314,7 +319,7 @@ sup {
}
td {
@extend %table-cell;
@extend %table-cell !optional;
}
}
} /* tbody */
@@ -329,7 +334,7 @@ sup {
height: 100%;
overflow: hidden;
@extend %rounded;
@extend %rounded !optional;
&:not(.no-bg) {
background: var(--img-bg);
@@ -340,7 +345,7 @@ sup {
-o-object-fit: cover;
object-fit: cover;
@extend %rounded;
@extend %rounded !optional;
@at-root #post-list & {
width: 100%;
@@ -349,14 +354,14 @@ sup {
}
.post-preview {
@extend %rounded;
@extend %rounded !optional;
border: 0;
background: var(--card-bg);
box-shadow: var(--card-shadow);
&::before {
@extend %rounded;
@extend %rounded !optional;
content: '';
width: 100%;
@@ -384,7 +389,7 @@ main {
p {
> a.popup {
&:not(.normal):not(.left):not(.right) {
@include align-center;
@include module.align-center;
}
}
}
@@ -393,22 +398,22 @@ main {
#tags,
#archives {
a:not(:hover) {
@extend %no-bottom-border;
@extend %no-bottom-border !optional;
}
}
}
.post-meta {
@extend %text-sm;
@extend %text-sm !optional;
a {
&:not([class]):hover {
@extend %link-hover;
@extend %link-hover !optional;
}
}
em {
@extend %normal-font-style;
@extend %normal-font-style !optional;
}
}
@@ -419,18 +424,18 @@ main {
a {
&.popup {
@extend %no-cursor;
@extend %img-caption;
@include mt-mb(0.5rem);
@extend %no-cursor !optional;
@extend %img-caption !optional;
@include module.mt-mb(0.5rem);
cursor: zoom-in;
}
&:not(.img-link) {
@extend %link-underline;
@extend %link-underline !optional;
&:hover {
@extend %link-hover;
@extend %link-hover !optional;
}
}
}
@@ -497,7 +502,7 @@ main {
} /* .content */
.tag:hover {
@extend %tag-hover;
@extend %tag-hover !optional;
}
.post-tag {
@@ -566,7 +571,7 @@ main {
margin-bottom: 1rem;
aspect-ratio: 16 / 9;
@extend %rounded;
@extend %rounded !optional;
&.twitch {
aspect-ratio: 310 / 189;
@@ -582,14 +587,14 @@ main {
margin-bottom: 0;
}
@extend %img-caption;
@extend %img-caption !optional;
}
.embed-audio {
width: 100%;
display: block;
@extend %img-caption;
@extend %img-caption !optional;
}
/* --- buttons --- */
@@ -680,15 +685,14 @@ $btn-border-width: 3px;
$btn-mb: 0.5rem;
#sidebar {
@include pl-pr(0);
@include module.pl-pr(0);
position: fixed;
top: 0;
left: 0;
height: 100%;
overflow-y: auto;
width: $sidebar-width;
z-index: 99;
width: variables.$sidebar-width;
background: var(--sidebar-bg);
border-right: 1px solid var(--sidebar-border-color);
@@ -708,7 +712,7 @@ $btn-mb: 0.5rem;
}
a {
@extend %sidebar-links;
@extend %sidebar-links !optional;
}
#avatar {
@@ -729,8 +733,8 @@ $btn-mb: 0.5rem;
}
.profile-wrapper {
@include mt-mb(2.5rem);
@extend %clickable-transition;
@include module.mt-mb(2.5rem);
@extend %clickable-transition !optional;
padding-left: 2.5rem;
padding-right: 1.25rem;
@@ -738,6 +742,9 @@ $btn-mb: 0.5rem;
}
.site-title {
@extend %clickable-transition !optional;
@extend %sidebar-link-hover !optional;
font-family: inherit;
font-weight: 900;
font-size: 1.75rem;
@@ -745,13 +752,8 @@ $btn-mb: 0.5rem;
letter-spacing: 0.25px;
margin-top: 1.25rem;
margin-bottom: 0.5rem;
a {
@extend %clickable-transition;
@extend %sidebar-link-hover;
color: var(--site-title-color);
}
width: fit-content;
color: var(--site-title-color);
}
.site-subtitle {
@@ -771,11 +773,11 @@ $btn-mb: 0.5rem;
li.nav-item {
opacity: 0.9;
width: 100%;
padding-left: 1.5rem;
padding-right: 1.5rem;
@include module.pl-pr(1.5rem);
a.nav-link {
@include pt-pb(0.6rem);
@include module.pt-pb(0.6rem);
display: flex;
align-items: center;
@@ -844,12 +846,12 @@ $btn-mb: 0.5rem;
}
a {
@extend %button;
@extend %sidebar-link-hover;
@extend %clickable-transition;
@extend %button !optional;
@extend %sidebar-link-hover !optional;
@extend %clickable-transition !optional;
&:not(:last-child) {
margin-right: $sb-btn-gap;
margin-right: variables.$sb-btn-gap;
}
}
@@ -858,14 +860,14 @@ $btn-mb: 0.5rem;
}
#mode-toggle {
@extend %button;
@extend %sidebar-links;
@extend %sidebar-link-hover;
@extend %button !optional;
@extend %sidebar-links !optional;
@extend %sidebar-link-hover !optional;
}
.icon-border {
@extend %no-cursor;
@include ml-mr(calc(($sb-btn-gap - $btn-border-width) / 2));
@extend %no-cursor !optional;
@include module.ml-mr(calc((variables.$sb-btn-gap - $btn-border-width) / 2));
background-color: var(--sidebar-btn-color);
content: '';
@@ -905,14 +907,12 @@ $btn-mb: 0.5rem;
/* --- top-bar --- */
#topbar-wrapper {
height: $topbar-height;
height: variables.$topbar-height;
background-color: var(--topbar-bg);
}
#topbar {
button i {
color: #999999;
}
@extend %btn-color !optional;
#breadcrumb {
font-size: 1rem;
@@ -920,7 +920,7 @@ $btn-mb: 0.5rem;
padding-left: 0.5rem;
a:hover {
@extend %link-hover;
@extend %link-hover !optional;
}
span {
@@ -935,43 +935,43 @@ $btn-mb: 0.5rem;
} /* #topbar */
::-webkit-input-placeholder {
@include placeholder;
@include module.placeholder;
}
::-moz-placeholder {
@include placeholder;
@include module.placeholder;
}
:-ms-input-placeholder {
@include placeholder;
@include module.placeholder;
}
::-ms-input-placeholder {
@include placeholder;
@include module.placeholder;
}
::placeholder {
@include placeholder;
@include module.placeholder;
}
:focus::-webkit-input-placeholder {
@include placeholder-focus;
@include module.placeholder-focus;
}
:focus::-moz-placeholder {
@include placeholder-focus;
@include module.placeholder-focus;
}
:focus:-ms-input-placeholder {
@include placeholder-focus;
@include module.placeholder-focus;
}
:focus::-ms-input-placeholder {
@include placeholder-focus;
@include module.placeholder-focus;
}
:focus::placeholder {
@include placeholder-focus;
@include module.placeholder-focus;
}
search {
@@ -1000,7 +1000,7 @@ search {
display: none;
white-space: nowrap;
@extend %cursor-pointer;
@extend %cursor-pointer !optional;
}
#search-input {
@@ -1038,7 +1038,7 @@ search {
padding-right: 0.2rem;
}
@extend %link-color;
@extend %link-color !optional;
}
}
@@ -1047,15 +1047,15 @@ search {
a {
font-size: 1.4rem;
line-height: 2.5rem;
line-height: 1.5rem;
&:hover {
@extend %link-hover;
@extend %link-hover !optional;
}
@extend %link-color;
@extend %no-bottom-border;
@extend %heading;
@extend %link-color !optional;
@extend %no-bottom-border !optional;
@extend %heading !optional;
}
> article {
@@ -1073,8 +1073,9 @@ search {
}
> p {
overflow: hidden;
text-overflow: ellipsis;
@extend %text-ellipsis !optional;
white-space: break-spaces;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
@@ -1090,23 +1091,11 @@ search {
color: var(--topbar-text-color);
text-align: center;
width: 70%;
overflow: hidden;
text-overflow: ellipsis;
word-break: keep-all;
white-space: nowrap;
}
#mask {
display: none;
position: fixed;
inset: 0 0 0 0;
height: 100%;
width: 100%;
z-index: 1;
@at-root [#{$sidebar-display}] & {
display: block !important;
}
}
/* --- basic wrappers --- */
@@ -1114,7 +1103,7 @@ search {
#main-wrapper {
position: relative;
@include pl-pr(0);
@include module.pl-pr(0);
> .container {
min-height: 100vh;
@@ -1124,7 +1113,7 @@ search {
#topbar-wrapper.row,
#main-wrapper > .container > .row,
#search-result-wrapper > .row {
@include ml-mr(0);
@include module.ml-mr(0);
}
#tail-wrapper {
@@ -1142,12 +1131,12 @@ search {
cursor: pointer;
position: fixed;
right: 1rem;
bottom: calc($footer-height-large - $back2top-size / 2);
bottom: calc(variables.$footer-height-large - variables.$back2top-size / 2);
background: var(--button-bg);
color: var(--btn-backtotop-color);
padding: 0;
width: $back2top-size;
height: $back2top-size;
width: variables.$back2top-size;
height: variables.$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;
@@ -1158,7 +1147,7 @@ search {
}
i {
line-height: $back2top-size;
line-height: variables.$back2top-size;
position: relative;
bottom: 2px;
}
@@ -1232,7 +1221,7 @@ search {
main {
.content {
> blockquote[class^='prompt-'] {
@include ml-mr(-1rem);
@include module.ml-mr(-1rem);
border-radius: 0;
max-width: none;
@@ -1252,12 +1241,12 @@ search {
}
#topbar {
@extend %full-width;
@extend %full-width !optional;
}
#main-wrapper > .container {
@extend %full-width;
@include pl-pr(0);
@extend %full-width !optional;
@include module.pl-pr(0);
}
}
@@ -1276,7 +1265,7 @@ search {
footer {
@include slide;
height: $footer-height-large;
height: variables.$footer-height-large;
padding: 1.5rem 0;
}
@@ -1286,7 +1275,7 @@ search {
}
#main-wrapper {
transform: translateX($sidebar-width);
transform: translateX(variables.$sidebar-width);
}
#back-to-top {
@@ -1297,8 +1286,8 @@ search {
#sidebar {
@include slide;
transform: translateX(-$sidebar-width); /* hide */
-webkit-transform: translateX(-$sidebar-width);
transform: translateX(-(variables.$sidebar-width)); /* hide */
-webkit-transform: translateX(-(variables.$sidebar-width));
}
#main-wrapper {
@@ -1361,7 +1350,7 @@ search {
}
#main-wrapper {
margin-left: $sidebar-width;
margin-left: variables.$sidebar-width;
}
#sidebar {
@@ -1375,11 +1364,11 @@ search {
}
search {
max-width: $search-max-width;
max-width: variables.$search-max-width;
}
#search-result-wrapper {
max-width: $main-content-max-width;
max-width: variables.$main-content-max-width;
justify-content: start !important;
}
@@ -1396,7 +1385,7 @@ search {
/* button 'back-to-Top' position */
#back-to-top {
right: 5%;
bottom: calc($footer-height - $back2top-size / 2);
bottom: calc(variables.$footer-height - variables.$back2top-size / 2);
}
#topbar-title {
@@ -1473,7 +1462,7 @@ search {
@media all and (min-width: 1400px) {
#back-to-top {
right: calc((100vw - $sidebar-width - 1140px) / 2 + 3rem);
right: calc((100vw - variables.$sidebar-width - 1140px) / 2 + 3rem);
}
}
@@ -1481,23 +1470,23 @@ search {
$icon-gap: 1rem;
#main-wrapper {
margin-left: $sidebar-width-large;
margin-left: variables.$sidebar-width-large;
}
#topbar-wrapper {
left: $sidebar-width-large;
left: variables.$sidebar-width-large;
}
search {
margin-right: calc(
$main-content-max-width / 4 - $search-max-width - 0.75rem
variables.$main-content-max-width / 4 - variables.$search-max-width - 0.75rem
);
}
#main-wrapper > .container {
max-width: $main-content-max-width;
padding-left: 1.75rem !important;
padding-right: 1.75rem !important;
max-width: variables.$main-content-max-width;
@include module.pl-pr(1.75rem, true);
}
main.col-12,
@@ -1507,12 +1496,12 @@ search {
#back-to-top {
right: calc(
(100vw - $sidebar-width-large - $main-content-max-width) / 2 + 2rem
(100vw - variables.$sidebar-width-large - variables.$main-content-max-width) / 2 + 2rem
);
}
#sidebar {
width: $sidebar-width-large;
width: variables.$sidebar-width-large;
.profile-wrapper {
margin-top: 3.5rem;
@@ -1522,7 +1511,7 @@ search {
ul {
li.nav-item {
@include pl-pr(2.75rem);
@include module.pl-pr(2.75rem);
}
}
@@ -1531,11 +1520,11 @@ search {
margin-bottom: 1.75rem;
a:not(:last-child) {
margin-right: $sb-btn-gap-lg;
margin-right: variables.$sb-btn-gap-lg;
}
.icon-border {
@include ml-mr(calc(($sb-btn-gap-lg - $btn-border-width) / 2));
@include module.ml-mr(calc((variables.$sb-btn-gap-lg - $btn-border-width) / 2));
}
}
}

View File

@@ -1,3 +1,5 @@
@use 'variables';
/*
* Mainly scss modules, only imported to `assets/css/main.scss`
*/
@@ -7,7 +9,8 @@
%heading {
color: var(--heading-color);
font-weight: 400;
font-family: $font-family-heading;
font-family: variables.$font-family-heading;
scroll-margin-top: 3.5rem;
}
%anchor {
@@ -81,7 +84,7 @@
}
%rounded {
border-radius: $radius-lg;
border-radius: variables.$radius-lg;
}
%img-caption {
@@ -111,6 +114,16 @@
-webkit-box-orient: vertical;
}
@mixin text-ellipsis {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
%text-ellipsis {
@include text-ellipsis;
}
%text-highlight {
color: var(--text-muted-highlight-color);
font-weight: 600;
@@ -134,6 +147,12 @@
}
}
%btn-color {
button i {
color: #999999;
}
}
/* ---------- scss mixin --------- */
@mixin mt-mb($value) {
@@ -151,9 +170,14 @@
padding-bottom: $val;
}
@mixin pl-pr($val) {
padding-left: $val;
padding-right: $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 {

View File

@@ -2,29 +2,31 @@
* The syntax highlight.
*/
@import 'colors/syntax-light';
@import 'colors/syntax-dark';
@use '../colors/syntax-light';
@use '../colors/syntax-dark';
@use 'module';
@use 'variables';
html {
@media (prefers-color-scheme: light) {
&:not([data-mode]),
&[data-mode='light'] {
@include light-syntax;
@include syntax-light.light-syntax;
}
&[data-mode='dark'] {
@include dark-syntax;
@include syntax-dark.dark-syntax;
}
}
@media (prefers-color-scheme: dark) {
&:not([data-mode]),
&[data-mode='dark'] {
@include dark-syntax;
@include syntax-dark.dark-syntax;
}
&[data-mode='light'] {
@include light-syntax;
@include syntax-light.light-syntax;
}
}
}
@@ -47,19 +49,19 @@ html {
}
.highlight {
@extend %rounded;
@extend %code-snippet-bg;
@extend %rounded !optional;
@extend %code-snippet-bg !optional;
overflow: auto;
padding-bottom: 0.75rem;
@at-root figure#{&} {
@extend %code-snippet-bg;
@extend %code-snippet-bg !optional;
}
pre {
margin-bottom: 0;
font-size: $code-font-size;
font-size: variables.$code-font-size;
line-height: 1.4rem;
word-wrap: normal; /* Fixed Safari overflow-x */
}
@@ -101,10 +103,10 @@ code {
color: var(--code-color);
&.highlighter-rouge {
font-size: $code-font-size;
font-size: variables.$code-font-size;
padding: 3px 5px;
word-break: break-word;
border-radius: $radius-sm;
border-radius: variables.$radius-sm;
background-color: var(--inline-code-bg);
}
@@ -130,7 +132,7 @@ code {
}
td.rouge-code {
@extend %code-snippet-padding;
@extend %code-snippet-padding !optional;
/*
Prevent some browser extends from
@@ -144,13 +146,13 @@ td.rouge-code {
}
div[class^='language-'] {
@extend %rounded;
@extend %code-snippet-bg;
@extend %rounded !optional;
@extend %code-snippet-bg !optional;
box-shadow: var(--language-border-color) 0 0 0 1px;
.content > & {
@include ml-mr(-1rem);
@include module.ml-mr(-1rem);
border-radius: 0;
}
@@ -179,23 +181,23 @@ div {
}
.code-header {
@extend %no-cursor;
@extend %no-cursor !optional;
display: flex;
justify-content: space-between;
align-items: center;
height: $code-header-height;
height: variables.$code-header-height;
margin-left: 0.75rem;
margin-right: 0.25rem;
/* the label block */
span {
line-height: $code-header-height;
line-height: variables.$code-header-height;
/* label icon */
i {
font-size: 1rem;
width: $code-icon-width;
width: variables.$code-icon-width;
color: var(--code-header-icon-color);
&.small {
@@ -219,12 +221,12 @@ div {
/* clipboard */
button {
@extend %cursor-pointer;
@extend %rounded;
@extend %cursor-pointer !optional;
@extend %rounded !optional;
border: 1px solid transparent;
height: $code-header-height;
width: $code-header-height;
height: variables.$code-header-height;
width: variables.$code-header-height;
padding: 0;
background-color: inherit;
@@ -259,13 +261,13 @@ div {
@media all and (min-width: 576px) {
div[class^='language-'] {
.content > & {
@include ml-mr(0);
@include module.ml-mr(0);
border-radius: $radius-lg;
border-radius: variables.$radius-lg;
}
.code-header {
@include ml-mr(0);
@include module.ml-mr(0);
$dot-margin: 1rem;
@@ -273,19 +275,19 @@ div {
content: '';
display: inline-block;
margin-left: $dot-margin;
width: $code-dot-size;
height: $code-dot-size;
width: variables.$code-dot-size;
height: variables.$code-dot-size;
border-radius: 50%;
background-color: var(--code-header-muted-color);
box-shadow: ($code-dot-size + $code-dot-gap) 0 0
box-shadow: (variables.$code-dot-size + variables.$code-dot-gap) 0 0
var(--code-header-muted-color),
($code-dot-size + $code-dot-gap) * 2 0 0
(variables.$code-dot-size + variables.$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);
margin-left: calc(($dot-margin + variables.$code-dot-size) / 2 * -1);
}
}
}

View File

@@ -22,7 +22,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 +59,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 */

View File

@@ -22,7 +22,6 @@
--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(
@@ -63,6 +62,7 @@
/* Posts */
--toc-highlight: #0550ae;
--toc-popup-border-color: lightgray;
--btn-share-color: gray;
--btn-share-hover-color: #0d6efd;
--card-bg: white;

View File

@@ -23,7 +23,7 @@
margin-left: -$timeline-width;
&::before {
@extend %timeline;
@extend %timeline !optional;
height: 72px;
left: 79px;
@@ -31,7 +31,7 @@
}
&:first-child::before {
@extend %timeline;
@extend %timeline !optional;
height: 32px;
top: 24px;
@@ -58,9 +58,8 @@
li {
font-size: 1.1rem;
line-height: 3rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
@extend %text-ellipsis !optional;
&:nth-child(odd) {
background-color: var(--main-bg, #ffffff);
@@ -75,7 +74,7 @@
}
&::before {
@extend %timeline;
@extend %timeline !optional;
top: 0;
left: 77px;

View File

@@ -1,3 +1,5 @@
@use '../addon/variables';
/*
Style for Tab Categories
*/
@@ -12,11 +14,11 @@
&.card,
.list-group {
@extend %rounded;
@extend %rounded !optional;
}
.card-header {
$radius: calc($radius-lg - 1px);
$radius: calc(variables.$radius-lg - 1px);
padding: 0.75rem;
border-radius: $radius;
@@ -29,7 +31,7 @@
}
i {
@extend %category-icon-color;
@extend %category-icon-color !optional;
font-size: 86%; /* fontawesome icons */
}

View File

@@ -1,3 +1,5 @@
@use '../addon/module';
/*
Style for page Category and Tag
*/
@@ -28,7 +30,7 @@
/* post's title */
> a {
@extend %no-bottom-border;
@extend %no-bottom-border !optional;
font-size: 1.1rem;
}
@@ -48,7 +50,7 @@
#page-tag,
#access-lastmod {
a:hover {
@extend %link-hover;
@extend %link-hover !optional;
margin-bottom: -1px; /* Avoid jumping */
}
@@ -63,9 +65,7 @@
}
> a {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
@include module.text-ellipsis;
}
}
}

View File

@@ -1,3 +1,5 @@
@use '../addon/variables';
/*
Style for Homepage
*/
@@ -20,14 +22,14 @@
background: none;
%img-radius {
border-radius: $radius-lg $radius-lg 0 0;
border-radius: variables.$radius-lg variables.$radius-lg 0 0;
}
.preview-img {
@extend %img-radius;
@extend %img-radius !optional;
img {
@extend %img-radius;
@extend %img-radius !optional;
}
}
@@ -36,7 +38,7 @@
padding: 1rem;
.card-title {
@extend %text-clip;
@extend %text-clip !optional;
color: var(--heading-color) !important;
font-size: 1.25rem;
@@ -47,10 +49,10 @@
}
.card-text.content {
@extend %muted;
@extend %muted !optional;
p {
@extend %text-clip;
@extend %text-clip !optional;
line-height: 1.5;
margin: 0;
@@ -58,7 +60,7 @@
}
.post-meta {
@extend %muted;
@extend %muted !optional;
i {
&:not(:first-child) {
@@ -67,16 +69,15 @@
}
em {
@extend %normal-font-style;
@extend %normal-font-style !optional;
color: inherit;
}
> div:first-child {
display: block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
@extend %text-ellipsis !optional;
}
}
}
@@ -131,7 +132,7 @@
/* Tablet */
@media all and (min-width: 768px) {
%img-radius {
border-radius: 0 $radius-lg $radius-lg 0;
border-radius: 0 variables.$radius-lg variables.$radius-lg 0;
}
#post-list {

View File

@@ -1,6 +1,9 @@
/*
Post-specific style
*/
@use '../addon/module';
@use '../addon/variables';
/**
* Post-specific styles
*/
%btn-post-nav {
width: 50%;
@@ -16,7 +19,7 @@
header {
.post-desc {
@extend %heading;
@extend %heading !optional;
font-size: 1.125rem;
line-height: 1.6;
@@ -29,7 +32,7 @@ header {
em,
time {
@extend %text-highlight;
@extend %text-highlight !optional;
}
em {
@@ -45,7 +48,7 @@ header {
}
.post-tail-wrapper {
@extend %text-sm;
@extend %text-sm !optional;
margin-top: 6rem;
border-bottom: 1px double var(--main-border-color);
@@ -54,20 +57,20 @@ header {
line-height: 1.2rem;
> a {
@extend %text-highlight;
@extend %text-highlight !optional;
&:hover {
@extend %link-hover;
@extend %link-hover !optional;
}
}
span:last-child {
@extend %text-sm;
@extend %text-sm !optional;
}
} /* .license-wrapper */
.post-meta a:not(:hover) {
@extend %link-underline;
@extend %link-underline !optional;
}
.share-wrapper {
@@ -87,17 +90,17 @@ header {
i {
color: var(--btn-share-color);
@extend %icon-size;
@extend %icon-size !optional;
}
> * {
@extend %icon-size;
@extend %icon-size !optional;
margin-left: 0.5rem;
&:hover {
i {
@extend %btn-share-hovor;
@extend %btn-share-hover !optional;
}
}
}
@@ -107,7 +110,7 @@ header {
border: none;
line-height: inherit;
@extend %cursor-pointer;
@extend %cursor-pointer !optional;
}
} /* .share-icons */
} /* .share-wrapper */
@@ -131,16 +134,16 @@ header {
.post-tag {
&:hover {
@extend %link-hover;
@extend %tag-hover;
@extend %no-bottom-border;
@extend %link-hover !optional;
@extend %tag-hover !optional;
@extend %no-bottom-border !optional;
}
}
}
.post-navigation {
.btn {
@extend %btn-post-nav;
@extend %btn-post-nav !optional;
&:not(:hover) {
color: var(--link-color);
@@ -153,7 +156,7 @@ header {
}
&.disabled {
@extend %btn-post-nav;
@extend %btn-post-nav !optional;
pointer-events: auto;
cursor: not-allowed;
@@ -173,12 +176,12 @@ header {
}
&:first-child {
border-radius: $radius-lg 0 0 $radius-lg;
border-radius: variables.$radius-lg 0 0 variables.$radius-lg;
left: 0.5px;
}
&:last-child {
border-radius: 0 $radius-lg $radius-lg 0;
border-radius: 0 variables.$radius-lg variables.$radius-lg 0;
right: 0.5px;
}
}
@@ -228,6 +231,7 @@ header {
}
}
/* TOC panel */
#toc-wrapper {
border-left: 1px solid rgba(158, 158, 158, 0.17);
position: -webkit-sticky;
@@ -257,9 +261,8 @@ header {
.toc-link {
display: block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
@extend %text-ellipsis !optional;
&:hover {
color: var(--toc-highlight);
@@ -290,25 +293,229 @@ header {
}
}
/* --- TOC button, bar and popup in mobile/tablet --- */
#toc-bar {
position: -webkit-sticky;
position: sticky;
top: 0;
z-index: 1;
margin: 0 -1rem;
height: variables.$topbar-height;
background: var(--main-bg);
border-bottom: 1px solid var(--main-border-color);
transition: all 0.2s ease-in-out;
@extend %btn-color !optional;
.label {
@extend %heading !optional;
margin-left: 0.25rem;
padding: 0 0.75rem;
color: inherit;
}
&.invisible {
top: -(variables.$topbar-height);
transition: none;
}
}
#toc-solo-trigger {
color: var(--text-muted-color);
border-color: var(--btn-border-color);
border-radius: variables.$radius-lg;
.label {
font-size: 1rem;
font-family: variables.$font-family-heading;
}
&:hover {
box-shadow: none;
background: none;
}
}
@mixin slide-in {
from {
opacity: 0.7;
transform: translateY(-(variables.$topbar-height));
}
to {
opacity: 1;
transform: translateY(0);
}
}
@mixin slide-out {
0% {
transform: translateY(0);
opacity: 1;
}
100% {
transform: translateY(-(variables.$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;
}
#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: variables.$radius-lg;
color: var(--text-color);
background: var(--card-bg);
margin-top: variables.$topbar-height;
min-width: 20rem;
font-size: 1.05rem;
@media all and (min-width: 576px) {
max-width: 32rem;
}
&[open] {
-webkit-animation: $slide-in;
animation: $slide-in;
}
&[closing] {
-webkit-animation: $slide-out;
animation: $slide-out;
}
@media all and (min-width: 850px) {
left: variables.$sidebar-width;
}
.header {
@extend %btn-color !optional;
position: -webkit-sticky;
position: sticky;
top: 0;
background-color: inherit;
border-bottom: 1px solid var(--main-border-color);
.label {
font-family: variables.$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 * variables.$topbar-height);
font-family: variables.$font-family-heading;
margin-bottom: -$curtain-height;
}
}
/* --- Related Posts --- */
#related-posts {
> h3 {
@include label(1.1rem, 600);
@include module.label(1.1rem, 600);
}
time {
@extend %normal-font-style;
@extend %text-xs;
@extend %normal-font-style !optional;
@extend %text-xs !optional;
color: var(--text-muted-color);
}
p {
@extend %text-ellipsis !optional;
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;
@@ -316,7 +523,7 @@ header {
.card {
h4 {
@extend %text-clip;
@extend %text-clip !optional;
}
}
}
@@ -328,14 +535,15 @@ 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 module.label(inherit, 400, inherit);
&::after {
content: ':';
@@ -362,9 +570,20 @@ header {
/* 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;
@include module.pl-pr(0);
@include module.ml-mr(-0.5rem);
}
}
@media all and (min-width: 1200px) {
h2,
h3,
h4 {
scroll-margin-top: 2rem;
}
#toc-bar,
#toc-solo-trigger {
display: none !important;
}
}

View File

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

View File

@@ -1,13 +1,13 @@
@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';
@use 'colors/typography-light';
@use 'colors/typography-dark';
@use 'addon/variables';
@use 'variables-hook';
@use 'addon/module';
@use 'addon/syntax';
@use 'addon/commons';
@use 'layout/home';
@use 'layout/post';
@use 'layout/tags';
@use 'layout/archives';
@use 'layout/categories';
@use 'layout/category-tag';

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>

View File

@@ -30,6 +30,7 @@
"devDependencies": {
"@babel/core": "^7.25.2",
"@babel/plugin-transform-class-properties": "^7.25.4",
"@babel/plugin-transform-private-methods": "^7.25.7",
"@babel/preset-env": "^7.25.4",
"@commitlint/cli": "^19.5.0",
"@commitlint/config-conventional": "^19.5.0",

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 })
];