chore: adopt Biome + Stylelint, modernize CI/CD, add security/support docs, minor JS fixes

- Linting/format
  - Replace ESLint/Prettier with Biome; remove old configs; add `biome.json`; update scripts
  - Tailwind-friendly `.stylelintrc.yaml`; Stylelint and Biome pass
  - Enable Biome across `modules/**`; prune legacy JS globals; ignore oversized `devicon.json`

- Code fixes
  - `hb-theme.js`: avoid assignment-in-expressions; clearer dataset updates
  - `hugoblox-slides.js`: fix inner var scope (var → let); apply formatting

- CI/CD
  - Split lint/build workflows; add path filters; Node via Corepack (pnpm from package.json)
  - Hugo pinned to env `HUGO_VERSION`; add consistency check vs `starters/academic-cv/hugoblox.yaml`
  - Nightly starters matrix build (academic-cv, landing-page); CodeQL weekly; resource caches

- Workflows/Community
  - Greeter: switch to `pull_request_target`; add Contributor Guide to PR message
  - PR labeler for path-based labels

- Repo policies/docs
  - `CODEOWNERS` for `@gcushen`
  - `SECURITY.md`, `SUPPORT.md`
  - Documented branch protection in `.github/settings.yml`
  - Move `CODE_OF_CONDUCT.md` to `.github/`

- docs(zh): update `README.zh.md` translation

Notes:
- pnpm is sourced from `package.json: packageManager` via Corepack; no hard-coded pnpm versions in CI.
This commit is contained in:
George Cushen committed 2025-09-17 03:17:38 +01:00
1 parent db8a975a9f
commit d5d2f04c2d
91 files changed
+2957 -1993

No files matched your search

@@ -1,85 +1,256 @@
/* Background */ .bg { color: #f8f8f2; background-color: #282a36 }
/* PreWrapper */ .chroma { color: #f8f8f2; background-color: #282a36; }
/* Other */ .chroma .x { }
/* Error */ .chroma .err { }
/* CodeLine */ .chroma .cl { }
/* LineTableTD */ .chroma .lntd { vertical-align: top; padding: 0; margin: 0; border: 0; }
/* LineTable */ .chroma .lntable { border-spacing: 0; padding: 0; margin: 0; border: 0; }
/* LineHighlight */ .chroma .hl { background-color: #ffc }
/* LineNumbersTable */ .chroma .lnt { white-space: pre; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f }
/* LineNumbers */ .chroma .ln { white-space: pre; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f }
/* Line */ .chroma .line { display: flex; }
/* Keyword */ .chroma .k { color: #ff79c6 }
/* KeywordConstant */ .chroma .kc { color: #ff79c6 }
/* KeywordDeclaration */ .chroma .kd { color: #8be9fd; font-style: italic }
/* KeywordNamespace */ .chroma .kn { color: #ff79c6 }
/* KeywordPseudo */ .chroma .kp { color: #ff79c6 }
/* KeywordReserved */ .chroma .kr { color: #ff79c6 }
/* KeywordType */ .chroma .kt { color: #8be9fd }
/* Name */ .chroma .n { }
/* NameAttribute */ .chroma .na { color: #50fa7b }
/* NameBuiltin */ .chroma .nb { color: #8be9fd; font-style: italic }
/* NameBuiltinPseudo */ .chroma .bp { }
/* NameClass */ .chroma .nc { color: #50fa7b }
/* NameConstant */ .chroma .no { }
/* NameDecorator */ .chroma .nd { }
/* NameEntity */ .chroma .ni { }
/* NameException */ .chroma .ne { }
/* NameFunction */ .chroma .nf { color: #50fa7b }
/* NameFunctionMagic */ .chroma .fm { }
/* NameLabel */ .chroma .nl { color: #8be9fd; font-style: italic }
/* NameNamespace */ .chroma .nn { }
/* NameOther */ .chroma .nx { }
/* NameProperty */ .chroma .py { }
/* NameTag */ .chroma .nt { color: #ff79c6 }
/* NameVariable */ .chroma .nv { color: #8be9fd; font-style: italic }
/* NameVariableClass */ .chroma .vc { color: #8be9fd; font-style: italic }
/* NameVariableGlobal */ .chroma .vg { color: #8be9fd; font-style: italic }
/* NameVariableInstance */ .chroma .vi { color: #8be9fd; font-style: italic }
/* NameVariableMagic */ .chroma .vm { }
/* Literal */ .chroma .l { }
/* LiteralDate */ .chroma .ld { }
/* LiteralString */ .chroma .s { color: #f1fa8c }
/* LiteralStringAffix */ .chroma .sa { color: #f1fa8c }
/* LiteralStringBacktick */ .chroma .sb { color: #f1fa8c }
/* LiteralStringChar */ .chroma .sc { color: #f1fa8c }
/* LiteralStringDelimiter */ .chroma .dl { color: #f1fa8c }
/* LiteralStringDoc */ .chroma .sd { color: #f1fa8c }
/* LiteralStringDouble */ .chroma .s2 { color: #f1fa8c }
/* LiteralStringEscape */ .chroma .se { color: #f1fa8c }
/* LiteralStringHeredoc */ .chroma .sh { color: #f1fa8c }
/* LiteralStringInterpol */ .chroma .si { color: #f1fa8c }
/* LiteralStringOther */ .chroma .sx { color: #f1fa8c }
/* LiteralStringRegex */ .chroma .sr { color: #f1fa8c }
/* LiteralStringSingle */ .chroma .s1 { color: #f1fa8c }
/* LiteralStringSymbol */ .chroma .ss { color: #f1fa8c }
/* LiteralNumber */ .chroma .m { color: #bd93f9 }
/* LiteralNumberBin */ .chroma .mb { color: #bd93f9 }
/* LiteralNumberFloat */ .chroma .mf { color: #bd93f9 }
/* LiteralNumberHex */ .chroma .mh { color: #bd93f9 }
/* LiteralNumberInteger */ .chroma .mi { color: #bd93f9 }
/* LiteralNumberIntegerLong */ .chroma .il { color: #bd93f9 }
/* LiteralNumberOct */ .chroma .mo { color: #bd93f9 }
/* Operator */ .chroma .o { color: #ff79c6 }
/* OperatorWord */ .chroma .ow { color: #ff79c6 }
/* Punctuation */ .chroma .p { }
/* Comment */ .chroma .c { color: #6272a4 }
/* CommentHashbang */ .chroma .ch { color: #6272a4 }
/* CommentMultiline */ .chroma .cm { color: #6272a4 }
/* CommentSingle */ .chroma .c1 { color: #6272a4 }
/* CommentSpecial */ .chroma .cs { color: #6272a4 }
/* CommentPreproc */ .chroma .cp { color: #ff79c6 }
/* CommentPreprocFile */ .chroma .cpf { color: #ff79c6 }
/* Generic */ .chroma .g { }
/* GenericDeleted */ .chroma .gd { color: #f55 }
/* GenericEmph */ .chroma .ge { text-decoration: underline }
/* GenericError */ .chroma .gr { }
/* GenericHeading */ .chroma .gh { font-weight: bold }
/* GenericInserted */ .chroma .gi { color: #50fa7b; font-weight: bold }
/* GenericOutput */ .chroma .go { color: #44475a }
/* GenericPrompt */ .chroma .gp { }
/* GenericStrong */ .chroma .gs { }
/* GenericSubheading */ .chroma .gu { font-weight: bold }
/* GenericTraceback */ .chroma .gt { }
/* GenericUnderline */ .chroma .gl { text-decoration: underline }
/* TextWhitespace */ .chroma .w { }
/* Background */ .bg {
color: #f8f8f2;
background-color: #282a36;
}
/* PreWrapper */ .chroma {
color: #f8f8f2;
background-color: #282a36;
}
/* Other */ .chroma .x {
}
/* Error */ .chroma .err {
}
/* CodeLine */ .chroma .cl {
}
/* LineTableTD */ .chroma .lntd {
vertical-align: top;
padding: 0;
margin: 0;
border: 0;
}
/* LineTable */ .chroma .lntable {
border-spacing: 0;
padding: 0;
margin: 0;
border: 0;
}
/* LineHighlight */ .chroma .hl {
background-color: #ffc;
}
/* LineNumbersTable */ .chroma .lnt {
white-space: pre;
user-select: none;
margin-right: 0.4em;
padding: 0 0.4em 0 0.4em;
color: #7f7f7f;
}
/* LineNumbers */ .chroma .ln {
white-space: pre;
user-select: none;
margin-right: 0.4em;
padding: 0 0.4em 0 0.4em;
color: #7f7f7f;
}
/* Line */ .chroma .line {
display: flex;
}
/* Keyword */ .chroma .k {
color: #ff79c6;
}
/* KeywordConstant */ .chroma .kc {
color: #ff79c6;
}
/* KeywordDeclaration */ .chroma .kd {
color: #8be9fd;
font-style: italic;
}
/* KeywordNamespace */ .chroma .kn {
color: #ff79c6;
}
/* KeywordPseudo */ .chroma .kp {
color: #ff79c6;
}
/* KeywordReserved */ .chroma .kr {
color: #ff79c6;
}
/* KeywordType */ .chroma .kt {
color: #8be9fd;
}
/* Name */ .chroma .n {
}
/* NameAttribute */ .chroma .na {
color: #50fa7b;
}
/* NameBuiltin */ .chroma .nb {
color: #8be9fd;
font-style: italic;
}
/* NameBuiltinPseudo */ .chroma .bp {
}
/* NameClass */ .chroma .nc {
color: #50fa7b;
}
/* NameConstant */ .chroma .no {
}
/* NameDecorator */ .chroma .nd {
}
/* NameEntity */ .chroma .ni {
}
/* NameException */ .chroma .ne {
}
/* NameFunction */ .chroma .nf {
color: #50fa7b;
}
/* NameFunctionMagic */ .chroma .fm {
}
/* NameLabel */ .chroma .nl {
color: #8be9fd;
font-style: italic;
}
/* NameNamespace */ .chroma .nn {
}
/* NameOther */ .chroma .nx {
}
/* NameProperty */ .chroma .py {
}
/* NameTag */ .chroma .nt {
color: #ff79c6;
}
/* NameVariable */ .chroma .nv {
color: #8be9fd;
font-style: italic;
}
/* NameVariableClass */ .chroma .vc {
color: #8be9fd;
font-style: italic;
}
/* NameVariableGlobal */ .chroma .vg {
color: #8be9fd;
font-style: italic;
}
/* NameVariableInstance */ .chroma .vi {
color: #8be9fd;
font-style: italic;
}
/* NameVariableMagic */ .chroma .vm {
}
/* Literal */ .chroma .l {
}
/* LiteralDate */ .chroma .ld {
}
/* LiteralString */ .chroma .s {
color: #f1fa8c;
}
/* LiteralStringAffix */ .chroma .sa {
color: #f1fa8c;
}
/* LiteralStringBacktick */ .chroma .sb {
color: #f1fa8c;
}
/* LiteralStringChar */ .chroma .sc {
color: #f1fa8c;
}
/* LiteralStringDelimiter */ .chroma .dl {
color: #f1fa8c;
}
/* LiteralStringDoc */ .chroma .sd {
color: #f1fa8c;
}
/* LiteralStringDouble */ .chroma .s2 {
color: #f1fa8c;
}
/* LiteralStringEscape */ .chroma .se {
color: #f1fa8c;
}
/* LiteralStringHeredoc */ .chroma .sh {
color: #f1fa8c;
}
/* LiteralStringInterpol */ .chroma .si {
color: #f1fa8c;
}
/* LiteralStringOther */ .chroma .sx {
color: #f1fa8c;
}
/* LiteralStringRegex */ .chroma .sr {
color: #f1fa8c;
}
/* LiteralStringSingle */ .chroma .s1 {
color: #f1fa8c;
}
/* LiteralStringSymbol */ .chroma .ss {
color: #f1fa8c;
}
/* LiteralNumber */ .chroma .m {
color: #bd93f9;
}
/* LiteralNumberBin */ .chroma .mb {
color: #bd93f9;
}
/* LiteralNumberFloat */ .chroma .mf {
color: #bd93f9;
}
/* LiteralNumberHex */ .chroma .mh {
color: #bd93f9;
}
/* LiteralNumberInteger */ .chroma .mi {
color: #bd93f9;
}
/* LiteralNumberIntegerLong */ .chroma .il {
color: #bd93f9;
}
/* LiteralNumberOct */ .chroma .mo {
color: #bd93f9;
}
/* Operator */ .chroma .o {
color: #ff79c6;
}
/* OperatorWord */ .chroma .ow {
color: #ff79c6;
}
/* Punctuation */ .chroma .p {
}
/* Comment */ .chroma .c {
color: #6272a4;
}
/* CommentHashbang */ .chroma .ch {
color: #6272a4;
}
/* CommentMultiline */ .chroma .cm {
color: #6272a4;
}
/* CommentSingle */ .chroma .c1 {
color: #6272a4;
}
/* CommentSpecial */ .chroma .cs {
color: #6272a4;
}
/* CommentPreproc */ .chroma .cp {
color: #ff79c6;
}
/* CommentPreprocFile */ .chroma .cpf {
color: #ff79c6;
}
/* Generic */ .chroma .g {
}
/* GenericDeleted */ .chroma .gd {
color: #f55;
}
/* GenericEmph */ .chroma .ge {
text-decoration: underline;
}
/* GenericError */ .chroma .gr {
}
/* GenericHeading */ .chroma .gh {
font-weight: bold;
}
/* GenericInserted */ .chroma .gi {
color: #50fa7b;
font-weight: bold;
}
/* GenericOutput */ .chroma .go {
color: #44475a;
}
/* GenericPrompt */ .chroma .gp {
}
/* GenericStrong */ .chroma .gs {
}
/* GenericSubheading */ .chroma .gu {
font-weight: bold;
}
/* GenericTraceback */ .chroma .gt {
}
/* GenericUnderline */ .chroma .gl {
text-decoration: underline;
}
/* TextWhitespace */ .chroma .w {
}
@@ -29,7 +29,7 @@
.chroma .hl {
width: 100%;
display: block;
background-color: rgba(110,118,129,0.1);
background-color: rgba(110, 118, 129, 0.1);
}
.chroma .lnt {
color: #8b949e;
@@ -1,85 +1,286 @@
/* Background */ .bg { background-color: #fff }
/* PreWrapper */ .chroma { background-color: #fff; }
/* Other */ .chroma .x { }
/* Error */ .chroma .err { color: #a61717; background-color: #e3d2d2 }
/* CodeLine */ .chroma .cl { }
/* LineTableTD */ .chroma .lntd { vertical-align: top; padding: 0; margin: 0; border: 0; }
/* LineTable */ .chroma .lntable { border-spacing: 0; padding: 0; margin: 0; border: 0; }
/* LineHighlight */ .chroma .hl { background-color: #ffc }
/* LineNumbersTable */ .chroma .lnt { white-space: pre; user-select: none; margin-right: 0.4em; padding: 0 0.4em;color: #7f7f7f }
/* LineNumbers */ .chroma .ln { white-space: pre; user-select: none; margin-right: 0.4em; padding: 0 0.4em;color: #7f7f7f }
/* Line */ .chroma .line { display: flex; }
/* Keyword */ .chroma .k { color: #000; font-weight: bold }
/* KeywordConstant */ .chroma .kc { color: #000; font-weight: bold }
/* KeywordDeclaration */ .chroma .kd { color: #000; font-weight: bold }
/* KeywordNamespace */ .chroma .kn { color: #000; font-weight: bold }
/* KeywordPseudo */ .chroma .kp { color: #000; font-weight: bold }
/* KeywordReserved */ .chroma .kr { color: #000; font-weight: bold }
/* KeywordType */ .chroma .kt { color: #458; font-weight: bold }
/* Name */ .chroma .n { }
/* NameAttribute */ .chroma .na { color: #008080 }
/* NameBuiltin */ .chroma .nb { color: #0086b3 }
/* NameBuiltinPseudo */ .chroma .bp { color: #999 }
/* NameClass */ .chroma .nc { color: #458; font-weight: bold }
/* NameConstant */ .chroma .no { color: #008080 }
/* NameDecorator */ .chroma .nd { color: #3c5d5d; font-weight: bold }
/* NameEntity */ .chroma .ni { color: #800080 }
/* NameException */ .chroma .ne { color: #900; font-weight: bold }
/* NameFunction */ .chroma .nf { color: #900; font-weight: bold }
/* NameFunctionMagic */ .chroma .fm { }
/* NameLabel */ .chroma .nl { color: #900; font-weight: bold }
/* NameNamespace */ .chroma .nn { color: #555 }
/* NameOther */ .chroma .nx { }
/* NameProperty */ .chroma .py { }
/* NameTag */ .chroma .nt { color: #000080 }
/* NameVariable */ .chroma .nv { color: #008080 }
/* NameVariableClass */ .chroma .vc { color: #008080 }
/* NameVariableGlobal */ .chroma .vg { color: #008080 }
/* NameVariableInstance */ .chroma .vi { color: #008080 }
/* NameVariableMagic */ .chroma .vm { }
/* Literal */ .chroma .l { }
/* LiteralDate */ .chroma .ld { }
/* LiteralString */ .chroma .s { color: #d14 }
/* LiteralStringAffix */ .chroma .sa { color: #d14 }
/* LiteralStringBacktick */ .chroma .sb { color: #d14 }
/* LiteralStringChar */ .chroma .sc { color: #d14 }
/* LiteralStringDelimiter */ .chroma .dl { color: #d14 }
/* LiteralStringDoc */ .chroma .sd { color: #d14 }
/* LiteralStringDouble */ .chroma .s2 { color: #d14 }
/* LiteralStringEscape */ .chroma .se { color: #d14 }
/* LiteralStringHeredoc */ .chroma .sh { color: #d14 }
/* LiteralStringInterpol */ .chroma .si { color: #d14 }
/* LiteralStringOther */ .chroma .sx { color: #d14 }
/* LiteralStringRegex */ .chroma .sr { color: #009926 }
/* LiteralStringSingle */ .chroma .s1 { color: #d14 }
/* LiteralStringSymbol */ .chroma .ss { color: #990073 }
/* LiteralNumber */ .chroma .m { color: #099 }
/* LiteralNumberBin */ .chroma .mb { color: #099 }
/* LiteralNumberFloat */ .chroma .mf { color: #099 }
/* LiteralNumberHex */ .chroma .mh { color: #099 }
/* LiteralNumberInteger */ .chroma .mi { color: #099 }
/* LiteralNumberIntegerLong */ .chroma .il { color: #099 }
/* LiteralNumberOct */ .chroma .mo { color: #099 }
/* Operator */ .chroma .o { color: #000; font-weight: bold }
/* OperatorWord */ .chroma .ow { color: #000; font-weight: bold }
/* Punctuation */ .chroma .p { }
/* Comment */ .chroma .c { color: #998; font-style: italic }
/* CommentHashbang */ .chroma .ch { color: #998; font-style: italic }
/* CommentMultiline */ .chroma .cm { color: #998; font-style: italic }
/* CommentSingle */ .chroma .c1 { color: #998; font-style: italic }
/* CommentSpecial */ .chroma .cs { color: #999; font-weight: bold; font-style: italic }
/* CommentPreproc */ .chroma .cp { color: #999; font-weight: bold; font-style: italic }
/* CommentPreprocFile */ .chroma .cpf { color: #999; font-weight: bold; font-style: italic }
/* Generic */ .chroma .g { }
/* GenericDeleted */ .chroma .gd { color: #000; background-color: #fdd }
/* GenericEmph */ .chroma .ge { color: #000; font-style: italic }
/* GenericError */ .chroma .gr { color: #a00 }
/* GenericHeading */ .chroma .gh { color: #999 }
/* GenericInserted */ .chroma .gi { color: #000; background-color: #dfd }
/* GenericOutput */ .chroma .go { color: #888 }
/* GenericPrompt */ .chroma .gp { color: #555 }
/* GenericStrong */ .chroma .gs { font-weight: bold }
/* GenericSubheading */ .chroma .gu { color: #aaa }
/* GenericTraceback */ .chroma .gt { color: #a00 }
/* GenericUnderline */ .chroma .gl { text-decoration: underline }
/* TextWhitespace */ .chroma .w { color: #bbb }
/* Background */ .bg {
background-color: #fff;
}
/* PreWrapper */ .chroma {
background-color: #fff;
}
/* Other */ .chroma .x {
}
/* Error */ .chroma .err {
color: #a61717;
background-color: #e3d2d2;
}
/* CodeLine */ .chroma .cl {
}
/* LineTableTD */ .chroma .lntd {
vertical-align: top;
padding: 0;
margin: 0;
border: 0;
}
/* LineTable */ .chroma .lntable {
border-spacing: 0;
padding: 0;
margin: 0;
border: 0;
}
/* LineHighlight */ .chroma .hl {
background-color: #ffc;
}
/* LineNumbersTable */ .chroma .lnt {
white-space: pre;
user-select: none;
margin-right: 0.4em;
padding: 0 0.4em;
color: #7f7f7f;
}
/* LineNumbers */ .chroma .ln {
white-space: pre;
user-select: none;
margin-right: 0.4em;
padding: 0 0.4em;
color: #7f7f7f;
}
/* Line */ .chroma .line {
display: flex;
}
/* Keyword */ .chroma .k {
color: #000;
font-weight: bold;
}
/* KeywordConstant */ .chroma .kc {
color: #000;
font-weight: bold;
}
/* KeywordDeclaration */ .chroma .kd {
color: #000;
font-weight: bold;
}
/* KeywordNamespace */ .chroma .kn {
color: #000;
font-weight: bold;
}
/* KeywordPseudo */ .chroma .kp {
color: #000;
font-weight: bold;
}
/* KeywordReserved */ .chroma .kr {
color: #000;
font-weight: bold;
}
/* KeywordType */ .chroma .kt {
color: #458;
font-weight: bold;
}
/* Name */ .chroma .n {
}
/* NameAttribute */ .chroma .na {
color: #008080;
}
/* NameBuiltin */ .chroma .nb {
color: #0086b3;
}
/* NameBuiltinPseudo */ .chroma .bp {
color: #999;
}
/* NameClass */ .chroma .nc {
color: #458;
font-weight: bold;
}
/* NameConstant */ .chroma .no {
color: #008080;
}
/* NameDecorator */ .chroma .nd {
color: #3c5d5d;
font-weight: bold;
}
/* NameEntity */ .chroma .ni {
color: #800080;
}
/* NameException */ .chroma .ne {
color: #900;
font-weight: bold;
}
/* NameFunction */ .chroma .nf {
color: #900;
font-weight: bold;
}
/* NameFunctionMagic */ .chroma .fm {
}
/* NameLabel */ .chroma .nl {
color: #900;
font-weight: bold;
}
/* NameNamespace */ .chroma .nn {
color: #555;
}
/* NameOther */ .chroma .nx {
}
/* NameProperty */ .chroma .py {
}
/* NameTag */ .chroma .nt {
color: #000080;
}
/* NameVariable */ .chroma .nv {
color: #008080;
}
/* NameVariableClass */ .chroma .vc {
color: #008080;
}
/* NameVariableGlobal */ .chroma .vg {
color: #008080;
}
/* NameVariableInstance */ .chroma .vi {
color: #008080;
}
/* NameVariableMagic */ .chroma .vm {
}
/* Literal */ .chroma .l {
}
/* LiteralDate */ .chroma .ld {
}
/* LiteralString */ .chroma .s {
color: #d14;
}
/* LiteralStringAffix */ .chroma .sa {
color: #d14;
}
/* LiteralStringBacktick */ .chroma .sb {
color: #d14;
}
/* LiteralStringChar */ .chroma .sc {
color: #d14;
}
/* LiteralStringDelimiter */ .chroma .dl {
color: #d14;
}
/* LiteralStringDoc */ .chroma .sd {
color: #d14;
}
/* LiteralStringDouble */ .chroma .s2 {
color: #d14;
}
/* LiteralStringEscape */ .chroma .se {
color: #d14;
}
/* LiteralStringHeredoc */ .chroma .sh {
color: #d14;
}
/* LiteralStringInterpol */ .chroma .si {
color: #d14;
}
/* LiteralStringOther */ .chroma .sx {
color: #d14;
}
/* LiteralStringRegex */ .chroma .sr {
color: #009926;
}
/* LiteralStringSingle */ .chroma .s1 {
color: #d14;
}
/* LiteralStringSymbol */ .chroma .ss {
color: #990073;
}
/* LiteralNumber */ .chroma .m {
color: #099;
}
/* LiteralNumberBin */ .chroma .mb {
color: #099;
}
/* LiteralNumberFloat */ .chroma .mf {
color: #099;
}
/* LiteralNumberHex */ .chroma .mh {
color: #099;
}
/* LiteralNumberInteger */ .chroma .mi {
color: #099;
}
/* LiteralNumberIntegerLong */ .chroma .il {
color: #099;
}
/* LiteralNumberOct */ .chroma .mo {
color: #099;
}
/* Operator */ .chroma .o {
color: #000;
font-weight: bold;
}
/* OperatorWord */ .chroma .ow {
color: #000;
font-weight: bold;
}
/* Punctuation */ .chroma .p {
}
/* Comment */ .chroma .c {
color: #998;
font-style: italic;
}
/* CommentHashbang */ .chroma .ch {
color: #998;
font-style: italic;
}
/* CommentMultiline */ .chroma .cm {
color: #998;
font-style: italic;
}
/* CommentSingle */ .chroma .c1 {
color: #998;
font-style: italic;
}
/* CommentSpecial */ .chroma .cs {
color: #999;
font-weight: bold;
font-style: italic;
}
/* CommentPreproc */ .chroma .cp {
color: #999;
font-weight: bold;
font-style: italic;
}
/* CommentPreprocFile */ .chroma .cpf {
color: #999;
font-weight: bold;
font-style: italic;
}
/* Generic */ .chroma .g {
}
/* GenericDeleted */ .chroma .gd {
color: #000;
background-color: #fdd;
}
/* GenericEmph */ .chroma .ge {
color: #000;
font-style: italic;
}
/* GenericError */ .chroma .gr {
color: #a00;
}
/* GenericHeading */ .chroma .gh {
color: #999;
}
/* GenericInserted */ .chroma .gi {
color: #000;
background-color: #dfd;
}
/* GenericOutput */ .chroma .go {
color: #888;
}
/* GenericPrompt */ .chroma .gp {
color: #555;
}
/* GenericStrong */ .chroma .gs {
font-weight: bold;
}
/* GenericSubheading */ .chroma .gu {
color: #aaa;
}
/* GenericTraceback */ .chroma .gt {
color: #a00;
}
/* GenericUnderline */ .chroma .gl {
text-decoration: underline;
}
/* TextWhitespace */ .chroma .w {
color: #bbb;
}
@@ -9,13 +9,19 @@
global RevealMarkdown, RevealSearch, RevealNotes, RevealMath, RevealZoom, Reveal, mermaid, RevealMenu
*/
import * as params from '@params';
import * as params from "@params";
// Enable core slide features.
var enabledPlugins = [RevealMarkdown, RevealSearch, RevealNotes, RevealMath.KaTeX, RevealZoom];
var enabledPlugins = [
RevealMarkdown,
RevealSearch,
RevealNotes,
RevealMath.KaTeX,
RevealZoom,
];
const isObject = function (o) {
return o === Object(o) && !isArray(o) && typeof o !== 'function';
return o === Object(o) && !isArray(o) && typeof o !== "function";
};
const isArray = function (a) {
@@ -24,7 +30,7 @@ const isArray = function (a) {
const toCamelCase = function (s) {
return s.replace(/([-_][a-z])/gi, function (term) {
return term.toUpperCase().replace('-', '').replace('_', '');
return term.toUpperCase().replace("-", "").replace("_", "");
});
};
@@ -48,14 +54,14 @@ const keysToCamelCase = function (o) {
// reveal configurations can be included in front matter under slides.reveal
var pluginOptions = {};
if (typeof params.slides.reveal_options !== 'undefined') {
if (typeof params.slides.reveal_options !== "undefined") {
pluginOptions = params.slides.reveal_options;
}
pluginOptions = keysToCamelCase(pluginOptions);
//enable menu by default if not set
if (typeof pluginOptions.menu_enabled === 'undefined') {
if (typeof pluginOptions.menu_enabled === "undefined") {
pluginOptions.menu_enabled = true;
}
@@ -64,12 +70,12 @@ if (pluginOptions.menu_enabled) {
enabledPlugins.push(RevealMenu);
}
pluginOptions['plugins'] = enabledPlugins;
pluginOptions["plugins"] = enabledPlugins;
Reveal.initialize(pluginOptions);
// Disable Mermaid by default.
if (typeof params.slides.diagram === 'undefined') {
if (typeof params.slides.diagram === "undefined") {
params.slides.diagram = false;
}
@@ -78,36 +84,40 @@ if (params.slides.diagram) {
//mermaid options
// mermaid: front matter configuration can be used to set mermaid options
// You can also use directives (see mermaid documentation)
var mermaidOptions = {};
if (typeof params.slides.diagram_options !== 'undefined') {
let mermaidOptions = {};
if (typeof params.slides.diagram_options !== "undefined") {
mermaidOptions = params.slides.diagram_options;
}
// `startOnLoad` must be false since diagrams are lazily rendered.
mermaidOptions['startOnLoad'] = false;
mermaidOptions["startOnLoad"] = false;
mermaid.initialize(mermaidOptions);
// Lazily render Mermaid diagrams within Reveal.JS slides
// See: https://github.com/hakimel/reveal.js/issues/2863#issuecomment-1107444425
let renderMermaidDiagrams = function renderMermaidDiagrams(event) {
let mermaidDivs = event.currentSlide.querySelectorAll('.mermaid:not(.done)');
let mermaidDivs = event.currentSlide.querySelectorAll(
".mermaid:not(.done)",
);
let indices = Reveal.getIndices();
let pageno = `${indices.h}-${indices.v}`
let pageno = `${indices.h}-${indices.v}`;
mermaidDivs.forEach(function (mermaidDiv, i) {
let insertSvg = function (svgCode) {
mermaidDiv.innerHTML = svgCode;
mermaidDiv.classList.add('done');
mermaidDiv.classList.add("done");
};
let graphDefinition = mermaidDiv.textContent;
mermaid.mermaidAPI.render(`mermaid${pageno}-${i}`, graphDefinition, insertSvg);
mermaid.mermaidAPI.render(
`mermaid${pageno}-${i}`,
graphDefinition,
insertSvg,
);
});
Reveal.layout();
};
Reveal.on('ready', event => renderMermaidDiagrams(event));
Reveal.on('slidechanged', event => renderMermaidDiagrams(event));
Reveal.on("ready", (event) => renderMermaidDiagrams(event));
Reveal.on("slidechanged", (event) => renderMermaidDiagrams(event));
}
@@ -87,7 +87,7 @@
/* Enhanced backdrop blur support */
@supports (backdrop-filter: blur(10px)) {
.resume-biography-modern [class*='backdrop-blur'] {
.resume-biography-modern [class*="backdrop-blur"] {
backdrop-filter: blur(10px);
}
}
@@ -152,12 +152,14 @@
font-size: 20px;
background-color: #fff;
color: #000; /* override parent alpha */
box-shadow: 0 10px 20px rgb(0 0 0 / 4%), 0 2px 6px rgb(0 0 0 / 4%), 0 0 1px rgb(0 0 0 / 4%);
box-shadow: 0 10px 20px rgb(0 0 0 / 4%), 0 2px 6px rgb(0 0 0 / 4%), 0 0 1px
rgb(0 0 0 / 4%);
}
.dark .avatar-emoji {
background-color: #000;
box-shadow: 0 10px 20px rgb(0 0 0 / 4%), 0 2px 6px rgb(0 0 0 / 4%), 0 0 1px rgb(255 255 255 / 96%);
box-shadow: 0 10px 20px rgb(0 0 0 / 4%), 0 2px 6px rgb(0 0 0 / 4%), 0 0 1px
rgb(255 255 255 / 96%);
}
/* Biography Block */
@@ -168,7 +170,7 @@
}
.hbx-bg-gradient::before {
content: '';
content: "";
position: absolute;
top: 0;
left: 0;
@@ -182,16 +184,22 @@
/* Light Mode Gradient */
.hbx-bg-gradient::before {
background-image: radial-gradient(at 20% 25%, hsla(212, 80%, 95%, 1) 0px, transparent 50%),
radial-gradient(at 75% 30%, hsla(160, 80%, 95%, 1) 0px, transparent 50%),
background-image: radial-gradient(
at 20% 25%,
hsla(212, 80%, 95%, 1) 0px,
transparent 50%
), radial-gradient(at 75% 30%, hsla(160, 80%, 95%, 1) 0px, transparent 50%),
radial-gradient(at 25% 80%, hsla(280, 80%, 95%, 1) 0px, transparent 50%),
radial-gradient(at 75% 85%, hsla(30, 80%, 95%, 1) 0px, transparent 50%);
}
/* Dark Mode Gradient */
.dark .hbx-bg-gradient::before {
background-image: radial-gradient(at 20% 25%, hsla(212, 35%, 15%, 1) 0px, transparent 50%),
radial-gradient(at 75% 30%, hsla(160, 35%, 15%, 1) 0px, transparent 50%),
background-image: radial-gradient(
at 20% 25%,
hsla(212, 35%, 15%, 1) 0px,
transparent 50%
), radial-gradient(at 75% 30%, hsla(160, 35%, 15%, 1) 0px, transparent 50%),
radial-gradient(at 25% 80%, hsla(280, 35%, 15%, 1) 0px, transparent 50%),
radial-gradient(at 75% 85%, hsla(30, 35%, 15%, 1) 0px, transparent 50%);
}
@@ -22,7 +22,7 @@
[data-search-toggle],
#search-toggle,
[data-te-collapse-init],
.nav-dropdown > .nav-link[role='button'] {
.nav-dropdown > .nav-link[role="button"] {
@apply cursor-pointer;
}
+2 -2
View File
@@ -1,5 +1,5 @@
@import './libs/chroma/light.css';
@import './libs/chroma/dark.css';
@import "./libs/chroma/light.css";
@import "./libs/chroma/dark.css";
/* Code Blocks: Apply theme background */
/* Requires relative position for Code Copy button */
@@ -1,10 +1,10 @@
@import 'page.css';
@import 'task-list.css';
@import 'copy.css';
@import 'sidebar-left.css';
@import 'charts.css';
@import 'steps.css';
@import 'cards.css';
@import 'math.css';
@import 'author-notes.css';
@import 'glassmorphism.css';
@import "page.css";
@import "task-list.css";
@import "copy.css";
@import "sidebar-left.css";
@import "charts.css";
@import "steps.css";
@import "cards.css";
@import "math.css";
@import "author-notes.css";
@import "glassmorphism.css";
@@ -35,7 +35,7 @@
.author-notes {
display: none;
}
/* Show author notes as superscript numbers in print */
.author-notes::after {
display: inline;
@@ -2,9 +2,11 @@
--hb-cols: 2;
}
.hb-cards {
grid-template-columns: repeat(auto-fill, minmax(max(250px, calc((100% - 1rem * 2) / var(--hb-cols))), 1fr));
grid-template-columns: repeat(
auto-fill,
minmax(max(250px, calc((100% - 1rem * 2) / var(--hb-cols))), 1fr)
);
}
.hb-card {
@@ -1,6 +1,6 @@
/**
* 2025 Hugo Blox Glassmorphism Utility Classes
*
*
* Modern glassmorphism effects for Hugo Blox CTA cards and other components
*/
@@ -21,9 +21,14 @@
}
.glassmorphism-primary::before {
content: '';
content: "";
@apply absolute inset-0 pointer-events-none;
background: linear-gradient(45deg, rgb(0 0 0 / 0.1), transparent, rgb(255 255 255 / 0.1));
background: linear-gradient(
45deg,
rgb(0 0 0 / 0.1),
transparent,
rgb(255 255 255 / 0.1)
);
mix-blend-mode: overlay;
}
@@ -40,37 +45,62 @@
}
.glassmorphism-secondary::before {
content: '';
content: "";
@apply absolute inset-0 pointer-events-none;
background: linear-gradient(45deg, rgb(0 0 0 / 0.1), transparent, rgb(255 255 255 / 0.1));
background: linear-gradient(
45deg,
rgb(0 0 0 / 0.1),
transparent,
rgb(255 255 255 / 0.1)
);
mix-blend-mode: overlay;
}
.glassmorphism-dark {
@apply relative overflow-hidden;
background: linear-gradient(135deg, rgb(0 0 0 / 0.7), rgb(31 41 55 / 0.8), rgb(0 0 0 / 0.9));
background: linear-gradient(
135deg,
rgb(0 0 0 / 0.7),
rgb(31 41 55 / 0.8),
rgb(0 0 0 / 0.9)
);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
}
.glassmorphism-dark::before {
content: '';
content: "";
@apply absolute inset-0 pointer-events-none;
background: linear-gradient(45deg, rgb(255 255 255 / 0.05), transparent, rgb(255 255 255 / 0.1));
background: linear-gradient(
45deg,
rgb(255 255 255 / 0.05),
transparent,
rgb(255 255 255 / 0.1)
);
mix-blend-mode: overlay;
}
.glassmorphism-light {
@apply relative overflow-hidden;
background: linear-gradient(135deg, rgb(255 255 255 / 0.9), rgb(248 250 252 / 0.95), rgb(241 245 249 / 0.9));
background: linear-gradient(
135deg,
rgb(255 255 255 / 0.9),
rgb(248 250 252 / 0.95),
rgb(241 245 249 / 0.9)
);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
}
.glassmorphism-light::before {
content: '';
content: "";
@apply absolute inset-0 pointer-events-none;
background: linear-gradient(45deg, rgb(0 0 0 / 0.02), transparent, rgb(255 255 255 / 0.05));
background: linear-gradient(
45deg,
rgb(0 0 0 / 0.02),
transparent,
rgb(255 255 255 / 0.05)
);
mix-blend-mode: overlay;
}
@@ -83,9 +113,9 @@
}
.noise-texture::after {
content: '';
content: "";
@apply absolute inset-0 pointer-events-none;
background-image: url('/media/textures/noise-pattern.svg');
background-image: url("/media/textures/noise-pattern.svg");
background-size: 100px 100px;
background-repeat: repeat;
opacity: 0.03;
@@ -106,13 +136,13 @@
}
.glass-shadow {
box-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.25), 0 0 0 1px rgb(255 255 255 / 0.1),
inset 0 1px 0 rgb(255 255 255 / 0.1);
box-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.25), 0 0 0 1px
rgb(255 255 255 / 0.1), inset 0 1px 0 rgb(255 255 255 / 0.1);
}
.dark .glass-shadow {
box-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.5), 0 0 0 1px rgb(255 255 255 / 0.05),
inset 0 1px 0 rgb(255 255 255 / 0.05);
box-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.5), 0 0 0 1px
rgb(255 255 255 / 0.05), inset 0 1px 0 rgb(255 255 255 / 0.05);
}
/* ==========================================================================
@@ -139,8 +169,8 @@
/* Modern border and shadow for light mode */
border: 1px solid rgb(255 255 255 / calc(var(--glassmorphism-opacity) + 0.1));
box-shadow: 0 32px 64px -12px rgb(0 0 0 / 0.25),
inset 0 1px 0 rgb(255 255 255 / calc(var(--glassmorphism-opacity) + 0.05));
box-shadow: 0 32px 64px -12px rgb(0 0 0 / 0.25), inset 0 1px 0
rgb(255 255 255 / calc(var(--glassmorphism-opacity) + 0.05));
}
.cta-glassmorphism:hover {
@@ -153,8 +183,8 @@
);
border: 1px solid rgb(255 255 255 / calc(var(--glassmorphism-opacity) + 0.2));
box-shadow: 0 40px 80px -16px rgb(0 0 0 / 0.3),
inset 0 1px 0 rgb(255 255 255 / calc(var(--glassmorphism-opacity) + 0.1));
box-shadow: 0 40px 80px -16px rgb(0 0 0 / 0.3), inset 0 1px 0
rgb(255 255 255 / calc(var(--glassmorphism-opacity) + 0.1));
}
/* Dark mode adjustments for CTA glassmorphism */
@@ -167,8 +197,8 @@
);
border: 1px solid rgb(255 255 255 / var(--glassmorphism-opacity));
box-shadow: 0 32px 64px -12px rgb(0 0 0 / 0.4),
inset 0 1px 0 rgb(255 255 255 / calc(var(--glassmorphism-opacity) * 0.5));
box-shadow: 0 32px 64px -12px rgb(0 0 0 / 0.4), inset 0 1px 0
rgb(255 255 255 / calc(var(--glassmorphism-opacity) * 0.5));
}
.dark .cta-glassmorphism:hover {
@@ -201,20 +231,23 @@
/* Light mode button background */
background: rgb(255 255 255 / 0.9);
border: 1px solid rgb(0 0 0 / 0.1);
box-shadow: 0 10px 25px -5px rgb(0 0 0 / 0.1), 0 4px 6px -2px rgb(0 0 0 / 0.1), inset 0 1px 0 rgb(255 255 255 / 0.3);
box-shadow: 0 10px 25px -5px rgb(0 0 0 / 0.1), 0 4px 6px -2px rgb(0 0 0 / 0.1),
inset 0 1px 0 rgb(255 255 255 / 0.3);
}
.dark .cta-glassmorphism .group > div:first-child {
/* Dark mode button background */
background: rgb(0 0 0 / 0.4);
border: 1px solid rgb(255 255 255 / 0.2);
box-shadow: 0 10px 25px -5px rgb(0 0 0 / 0.3), 0 4px 6px -2px rgb(0 0 0 / 0.2), inset 0 1px 0 rgb(255 255 255 / 0.1);
box-shadow: 0 10px 25px -5px rgb(0 0 0 / 0.3), 0 4px 6px -2px rgb(0 0 0 / 0.2),
inset 0 1px 0 rgb(255 255 255 / 0.1);
}
.cta-glassmorphism .group:hover > div:first-child {
background: rgb(255 255 255);
border: 1px solid rgb(0 0 0 / 0.15);
box-shadow: 0 20px 40px -8px rgb(0 0 0 / 0.15), 0 8px 12px -4px rgb(0 0 0 / 0.1), inset 0 1px 0 rgb(255 255 255 / 0.4);
box-shadow: 0 20px 40px -8px rgb(0 0 0 / 0.15), 0 8px 12px -4px
rgb(0 0 0 / 0.1), inset 0 1px 0 rgb(255 255 255 / 0.4);
}
.dark .cta-glassmorphism .group:hover > div:first-child {
@@ -1,2 +1,4 @@
/* Prevent long equations overflowing on small screens by scrolling horizontally instead. */
.katex-display { overflow: auto hidden; }
.katex-display {
overflow: auto hidden;
}
@@ -7,11 +7,11 @@ ul.task-list {
list-style: none;
}
ul.task-list li input[type='checkbox'] {
ul.task-list li input[type="checkbox"] {
margin-right: 0.5rem;
}
ul.task-list input[type='checkbox']:checked {
ul.task-list input[type="checkbox"]:checked {
appearance: none;
width: 1em;
height: 1em;
@@ -20,11 +20,11 @@ ul.task-list input[type='checkbox']:checked {
position: relative;
}
ul.task-list input[type='checkbox']:not(:checked) {
ul.task-list input[type="checkbox"]:not(:checked) {
width: 0.9em;
height: 0.9em;
}
ul.task-list input[type='checkbox']:checked::after {
content: '✅';
ul.task-list input[type="checkbox"]:checked::after {
content: "✅";
}
@@ -220,11 +220,11 @@
/* Support for dual light/dark background colors in parse_block_v2.html */
@media (prefers-color-scheme: dark) {
.home-section-bg[style*='--dark-bg-color'] {
.home-section-bg[style*="--dark-bg-color"] {
background-color: var(--dark-bg-color) !important;
}
}
.dark .home-section-bg[style*='--dark-bg-color'] {
.dark .home-section-bg[style*="--dark-bg-color"] {
background-color: var(--dark-bg-color) !important;
}
@@ -320,7 +320,8 @@
/* Note: Providing these palettes in @theme ensures theme-pack mappings like var(--color-rose-600) always resolve */
/* Font family */
--font-family-sans: inter var, ui-sans-serif, system-ui, sans-serif, apple color emoji, segoe ui emoji,
--font-family-sans:
inter var, ui-sans-serif, system-ui, sans-serif, apple color emoji, segoe ui emoji,
segoe ui symbol, noto color emoji;
/* Font sizes */
@@ -5,7 +5,7 @@
-moz-osx-font-smoothing: grayscale;
text-rendering: optimizelegibility;
}
body {
line-height: 1.5;
}
@@ -25,28 +25,28 @@
text-decoration-color: rgb(var(--color-primary-300));
font-weight: 500;
}
.prose a:hover {
color: rgb(var(--color-primary-600));
text-decoration: none;
border-radius: 0.09rem;
}
.prose mark {
color: rgb(var(--color-neutral-900));
background-color: rgb(var(--color-primary-200));
padding: 0.1rem 0.2rem;
border-radius: 0.25rem;
}
.dark .prose-invert a {
text-decoration-color: rgb(var(--color-neutral-500));
}
.dark .prose-invert a:hover {
color: rgb(var(--color-primary-300));
}
.dark .prose-invert mark {
background-color: rgb(var(--color-primary-400));
color: black;
@@ -28,7 +28,7 @@
}
/* 2025 Glassmorphism texture support - disable conflicting defaults */
.home-section-bg.bg-image[style*='background-size'][style*='background-repeat'] {
.home-section-bg.bg-image[style*="background-size"][style*="background-repeat"] {
background-size: revert !important;
background-repeat: revert !important;
background-position: revert !important;
@@ -39,7 +39,7 @@
/* LineHighlight */
.chroma .hl {
background-color: #ffc
background-color: #ffc;
}
/* LineNumbersTable */
@@ -49,7 +49,7 @@
user-select: none;
margin-right: 0.4em;
padding: 0 0.4em 0 0.4em;
color: #7f7f7f
color: #7f7f7f;
}
/* LineNumbers */
@@ -59,7 +59,7 @@
user-select: none;
margin-right: 0.4em;
padding: 0 0.4em 0 0.4em;
color: #7f7f7f
color: #7f7f7f;
}
/* Line */
@@ -71,44 +71,44 @@
/* Keyword */
.chroma .k {
color: #ff79c6
color: #ff79c6;
}
/* KeywordConstant */
.chroma .kc {
color: #ff79c6
color: #ff79c6;
}
/* KeywordDeclaration */
.chroma .kd {
color: #8be9fd;
font-style: italic
font-style: italic;
}
/* KeywordNamespace */
.chroma .kn {
color: #ff79c6
color: #ff79c6;
}
/* KeywordPseudo */
.chroma .kp {
color: #ff79c6
color: #ff79c6;
}
/* KeywordReserved */
.chroma .kr {
color: #ff79c6
color: #ff79c6;
}
/* KeywordType */
.chroma .kt {
color: #8be9fd
color: #8be9fd;
}
/* Name */
@@ -119,14 +119,14 @@
/* NameAttribute */
.chroma .na {
color: #50fa7b
color: #50fa7b;
}
/* NameBuiltin */
.chroma .nb {
color: #8be9fd;
font-style: italic
font-style: italic;
}
/* NameBuiltinPseudo */
@@ -137,7 +137,7 @@
/* NameClass */
.chroma .nc {
color: #50fa7b
color: #50fa7b;
}
/* NameConstant */
@@ -163,7 +163,7 @@
/* NameFunction */
.chroma .nf {
color: #50fa7b
color: #50fa7b;
}
/* NameFunctionMagic */
@@ -175,7 +175,7 @@
.chroma .nl {
color: #8be9fd;
font-style: italic
font-style: italic;
}
/* NameNamespace */
@@ -196,35 +196,35 @@
/* NameTag */
.chroma .nt {
color: #ff79c6
color: #ff79c6;
}
/* NameVariable */
.chroma .nv {
color: #8be9fd;
font-style: italic
font-style: italic;
}
/* NameVariableClass */
.chroma .vc {
color: #8be9fd;
font-style: italic
font-style: italic;
}
/* NameVariableGlobal */
.chroma .vg {
color: #8be9fd;
font-style: italic
font-style: italic;
}
/* NameVariableInstance */
.chroma .vi {
color: #8be9fd;
font-style: italic
font-style: italic;
}
/* NameVariableMagic */
@@ -245,139 +245,139 @@
/* LiteralString */
.chroma .s {
color: #f1fa8c
color: #f1fa8c;
}
/* LiteralStringAffix */
.chroma .sa {
color: #f1fa8c
color: #f1fa8c;
}
/* LiteralStringBacktick */
.chroma .sb {
color: #f1fa8c
color: #f1fa8c;
}
/* LiteralStringChar */
.chroma .sc {
color: #f1fa8c
color: #f1fa8c;
}
/* LiteralStringDelimiter */
.chroma .dl {
color: #f1fa8c
color: #f1fa8c;
}
/* LiteralStringDoc */
.chroma .sd {
color: #f1fa8c
color: #f1fa8c;
}
/* LiteralStringDouble */
.chroma .s2 {
color: #f1fa8c
color: #f1fa8c;
}
/* LiteralStringEscape */
.chroma .se {
color: #f1fa8c
color: #f1fa8c;
}
/* LiteralStringHeredoc */
.chroma .sh {
color: #f1fa8c
color: #f1fa8c;
}
/* LiteralStringInterpol */
.chroma .si {
color: #f1fa8c
color: #f1fa8c;
}
/* LiteralStringOther */
.chroma .sx {
color: #f1fa8c
color: #f1fa8c;
}
/* LiteralStringRegex */
.chroma .sr {
color: #f1fa8c
color: #f1fa8c;
}
/* LiteralStringSingle */
.chroma .s1 {
color: #f1fa8c
color: #f1fa8c;
}
/* LiteralStringSymbol */
.chroma .ss {
color: #f1fa8c
color: #f1fa8c;
}
/* LiteralNumber */
.chroma .m {
color: #bd93f9
color: #bd93f9;
}
/* LiteralNumberBin */
.chroma .mb {
color: #bd93f9
color: #bd93f9;
}
/* LiteralNumberFloat */
.chroma .mf {
color: #bd93f9
color: #bd93f9;
}
/* LiteralNumberHex */
.chroma .mh {
color: #bd93f9
color: #bd93f9;
}
/* LiteralNumberInteger */
.chroma .mi {
color: #bd93f9
color: #bd93f9;
}
/* LiteralNumberIntegerLong */
.chroma .il {
color: #bd93f9
color: #bd93f9;
}
/* LiteralNumberOct */
.chroma .mo {
color: #bd93f9
color: #bd93f9;
}
/* Operator */
.chroma .o {
color: #ff79c6
color: #ff79c6;
}
/* OperatorWord */
.chroma .ow {
color: #ff79c6
color: #ff79c6;
}
/* Punctuation */
@@ -388,43 +388,43 @@
/* Comment */
.chroma .c {
color: #6272a4
color: #6272a4;
}
/* CommentHashbang */
.chroma .ch {
color: #6272a4
color: #6272a4;
}
/* CommentMultiline */
.chroma .cm {
color: #6272a4
color: #6272a4;
}
/* CommentSingle */
.chroma .c1 {
color: #6272a4
color: #6272a4;
}
/* CommentSpecial */
.chroma .cs {
color: #6272a4
color: #6272a4;
}
/* CommentPreproc */
.chroma .cp {
color: #ff79c6
color: #ff79c6;
}
/* CommentPreprocFile */
.chroma .cpf {
color: #ff79c6
color: #ff79c6;
}
/* Generic */
@@ -435,13 +435,13 @@
/* GenericDeleted */
.chroma .gd {
color: #f55
color: #f55;
}
/* GenericEmph */
.chroma .ge {
text-decoration: underline
text-decoration: underline;
}
/* GenericError */
@@ -452,20 +452,20 @@
/* GenericHeading */
.chroma .gh {
font-weight: bold
font-weight: bold;
}
/* GenericInserted */
.chroma .gi {
color: #50fa7b;
font-weight: bold
font-weight: bold;
}
/* GenericOutput */
.chroma .go {
color: #44475a
color: #44475a;
}
/* GenericPrompt */
@@ -481,7 +481,7 @@
/* GenericSubheading */
.chroma .gu {
font-weight: bold
font-weight: bold;
}
/* GenericTraceback */
@@ -492,7 +492,7 @@
/* GenericUnderline */
.chroma .gl {
text-decoration: underline
text-decoration: underline;
}
/* TextWhitespace */
@@ -12,7 +12,7 @@ html:not(.dark) .highlight {
.chroma .err {
color: #a61717;
background-color: #e3d2d2
background-color: #e3d2d2;
}
/* CodeLine */
@@ -41,7 +41,7 @@ html:not(.dark) .highlight {
/* LineHighlight */
.chroma .hl {
background-color: #ffc
background-color: #ffc;
}
/* LineNumbersTable */
@@ -51,7 +51,7 @@ html:not(.dark) .highlight {
user-select: none;
margin-right: 0.4em;
padding: 0 0.4em;
color: #7f7f7f
color: #7f7f7f;
}
/* LineNumbers */
@@ -61,7 +61,7 @@ html:not(.dark) .highlight {
user-select: none;
margin-right: 0.4em;
padding: 0 0.4em;
color: #7f7f7f
color: #7f7f7f;
}
/* Line */
@@ -74,49 +74,49 @@ html:not(.dark) .highlight {
.chroma .k {
color: #000;
font-weight: bold
font-weight: bold;
}
/* KeywordConstant */
.chroma .kc {
color: #000;
font-weight: bold
font-weight: bold;
}
/* KeywordDeclaration */
.chroma .kd {
color: #000;
font-weight: bold
font-weight: bold;
}
/* KeywordNamespace */
.chroma .kn {
color: #000;
font-weight: bold
font-weight: bold;
}
/* KeywordPseudo */
.chroma .kp {
color: #000;
font-weight: bold
font-weight: bold;
}
/* KeywordReserved */
.chroma .kr {
color: #000;
font-weight: bold
font-weight: bold;
}
/* KeywordType */
.chroma .kt {
color: #458;
font-weight: bold
font-weight: bold;
}
/* Name */
@@ -127,59 +127,59 @@ html:not(.dark) .highlight {
/* NameAttribute */
.chroma .na {
color: #008080
color: #008080;
}
/* NameBuiltin */
.chroma .nb {
color: #0086b3
color: #0086b3;
}
/* NameBuiltinPseudo */
.chroma .bp {
color: #999
color: #999;
}
/* NameClass */
.chroma .nc {
color: #458;
font-weight: bold
font-weight: bold;
}
/* NameConstant */
.chroma .no {
color: #008080
color: #008080;
}
/* NameDecorator */
.chroma .nd {
color: #3c5d5d;
font-weight: bold
font-weight: bold;
}
/* NameEntity */
.chroma .ni {
color: #800080
color: #800080;
}
/* NameException */
.chroma .ne {
color: #900;
font-weight: bold
font-weight: bold;
}
/* NameFunction */
.chroma .nf {
color: #900;
font-weight: bold
font-weight: bold;
}
/* NameFunctionMagic */
@@ -191,13 +191,13 @@ html:not(.dark) .highlight {
.chroma .nl {
color: #900;
font-weight: bold
font-weight: bold;
}
/* NameNamespace */
.chroma .nn {
color: #555
color: #555;
}
/* NameOther */
@@ -213,31 +213,31 @@ html:not(.dark) .highlight {
/* NameTag */
.chroma .nt {
color: #000080
color: #000080;
}
/* NameVariable */
.chroma .nv {
color: #008080
color: #008080;
}
/* NameVariableClass */
.chroma .vc {
color: #008080
color: #008080;
}
/* NameVariableGlobal */
.chroma .vg {
color: #008080
color: #008080;
}
/* NameVariableInstance */
.chroma .vi {
color: #008080
color: #008080;
}
/* NameVariableMagic */
@@ -258,141 +258,141 @@ html:not(.dark) .highlight {
/* LiteralString */
.chroma .s {
color: #d14
color: #d14;
}
/* LiteralStringAffix */
.chroma .sa {
color: #d14
color: #d14;
}
/* LiteralStringBacktick */
.chroma .sb {
color: #d14
color: #d14;
}
/* LiteralStringChar */
.chroma .sc {
color: #d14
color: #d14;
}
/* LiteralStringDelimiter */
.chroma .dl {
color: #d14
color: #d14;
}
/* LiteralStringDoc */
.chroma .sd {
color: #d14
color: #d14;
}
/* LiteralStringDouble */
.chroma .s2 {
color: #d14
color: #d14;
}
/* LiteralStringEscape */
.chroma .se {
color: #d14
color: #d14;
}
/* LiteralStringHeredoc */
.chroma .sh {
color: #d14
color: #d14;
}
/* LiteralStringInterpol */
.chroma .si {
color: #d14
color: #d14;
}
/* LiteralStringOther */
.chroma .sx {
color: #d14
color: #d14;
}
/* LiteralStringRegex */
.chroma .sr {
color: #009926
color: #009926;
}
/* LiteralStringSingle */
.chroma .s1 {
color: #d14
color: #d14;
}
/* LiteralStringSymbol */
.chroma .ss {
color: #990073
color: #990073;
}
/* LiteralNumber */
.chroma .m {
color: #099
color: #099;
}
/* LiteralNumberBin */
.chroma .mb {
color: #099
color: #099;
}
/* LiteralNumberFloat */
.chroma .mf {
color: #099
color: #099;
}
/* LiteralNumberHex */
.chroma .mh {
color: #099
color: #099;
}
/* LiteralNumberInteger */
.chroma .mi {
color: #099
color: #099;
}
/* LiteralNumberIntegerLong */
.chroma .il {
color: #099
color: #099;
}
/* LiteralNumberOct */
.chroma .mo {
color: #099
color: #099;
}
/* Operator */
.chroma .o {
color: #000;
font-weight: bold
font-weight: bold;
}
/* OperatorWord */
.chroma .ow {
color: #000;
font-weight: bold
font-weight: bold;
}
/* Punctuation */
@@ -404,28 +404,28 @@ html:not(.dark) .highlight {
.chroma .c {
color: #998;
font-style: italic
font-style: italic;
}
/* CommentHashbang */
.chroma .ch {
color: #998;
font-style: italic
font-style: italic;
}
/* CommentMultiline */
.chroma .cm {
color: #998;
font-style: italic
font-style: italic;
}
/* CommentSingle */
.chroma .c1 {
color: #998;
font-style: italic
font-style: italic;
}
/* CommentSpecial */
@@ -433,7 +433,7 @@ html:not(.dark) .highlight {
.chroma .cs {
color: #999;
font-weight: bold;
font-style: italic
font-style: italic;
}
/* CommentPreproc */
@@ -441,7 +441,7 @@ html:not(.dark) .highlight {
.chroma .cp {
color: #999;
font-weight: bold;
font-style: italic
font-style: italic;
}
/* CommentPreprocFile */
@@ -449,7 +449,7 @@ html:not(.dark) .highlight {
.chroma .cpf {
color: #999;
font-weight: bold;
font-style: italic
font-style: italic;
}
/* Generic */
@@ -461,74 +461,74 @@ html:not(.dark) .highlight {
.chroma .gd {
color: #000;
background-color: #fdd
background-color: #fdd;
}
/* GenericEmph */
.chroma .ge {
color: #000;
font-style: italic
font-style: italic;
}
/* GenericError */
.chroma .gr {
color: #a00
color: #a00;
}
/* GenericHeading */
.chroma .gh {
color: #999
color: #999;
}
/* GenericInserted */
.chroma .gi {
color: #000;
background-color: #dfd
background-color: #dfd;
}
/* GenericOutput */
.chroma .go {
color: #888
color: #888;
}
/* GenericPrompt */
.chroma .gp {
color: #555
color: #555;
}
/* GenericStrong */
.chroma .gs {
font-weight: bold
font-weight: bold;
}
/* GenericSubheading */
.chroma .gu {
color: #aaa
color: #aaa;
}
/* GenericTraceback */
.chroma .gt {
color: #a00
color: #a00;
}
/* GenericUnderline */
.chroma .gl {
text-decoration: underline
text-decoration: underline;
}
/* TextWhitespace */
.chroma .w {
color: #bbb
color: #bbb;
}
}
+11 -11
View File
@@ -11,26 +11,26 @@
*/
/* Tailwind CSS v4 Base */
@import 'tailwindcss';
@import "tailwindcss";
/* Configuration */
@import 'config/tailwind.css';
@import 'config/theme.css';
@import "config/tailwind.css";
@import "config/theme.css";
/* Base styles and utilities */
@import 'framework/base.css';
@import "framework/base.css";
/* Hugo Blox theme system (color utilities) */
@import 'color-utilities.css';
@import "color-utilities.css";
/* Hugo Blox framework components */
@import 'framework/components.css';
@import "framework/components.css";
/* Hugo Blox module components */
@import 'components/all.css';
@import 'blox/all.css';
@import 'views/all.css';
@import 'chroma.css';
@import "components/all.css";
@import "blox/all.css";
@import "views/all.css";
@import "chroma.css";
/* Safelist for dynamic classes */
@import 'config/safelist.css';
@import "config/safelist.css";
+50 -45
View File
@@ -3,31 +3,31 @@
* Copies BibTeX citation to clipboard when cite button is clicked
*/
import { hugoEnvironment, i18n } from '@params';
import { showNotification } from './hb-notifier.js';
import { copyToClipboardSync, ClipboardCache } from './hb-clipboard.js';
import { hugoEnvironment, i18n } from "@params";
import { ClipboardCache, copyToClipboardSync } from "./hb-clipboard.js";
import { showNotification } from "./hb-notifier.js";
// Debug mode based on environment
const isDebugMode = hugoEnvironment === 'development';
const isDebugMode = hugoEnvironment === "development";
// Cache for citation content
const citationCache = new ClipboardCache();
// Initialize citation handlers when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initializeCitation);
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", initializeCitation);
} else {
initializeCitation();
}
function initializeCitation() {
// Handle citation button clicks using event delegation
document.addEventListener('click', handleCiteClick);
document.addEventListener("click", handleCiteClick);
// Prefetch citations on hover/focus for better UX and Safari compatibility
document.addEventListener('mouseover', prefetchOnHover);
document.addEventListener('focusin', prefetchOnHover);
document.addEventListener("mouseover", prefetchOnHover);
document.addEventListener("focusin", prefetchOnHover);
// Prefetch all citations on page load
prefetchAllCitations();
}
@@ -36,9 +36,11 @@ function initializeCitation() {
* Prefetch all citations on page load
*/
function prefetchAllCitations() {
const citeButtons = document.querySelectorAll('.js-cite-clipboard[data-filename]');
citeButtons.forEach(button => {
const filename = button.getAttribute('data-filename');
const citeButtons = document.querySelectorAll(
".js-cite-clipboard[data-filename]",
);
citeButtons.forEach((button) => {
const filename = button.getAttribute("data-filename");
if (filename && !citationCache.has(filename)) {
// Fetch in background, don't await
fetchAndCacheCitation(filename);
@@ -51,10 +53,10 @@ function prefetchAllCitations() {
* @param {Event} e - Hover or focus event
*/
function prefetchOnHover(e) {
const citeButton = e.target.closest('.js-cite-clipboard');
const citeButton = e.target.closest(".js-cite-clipboard");
if (!citeButton) return;
const filename = citeButton.getAttribute('data-filename');
const filename = citeButton.getAttribute("data-filename");
if (filename && !citationCache.has(filename)) {
// Fetch in background, don't await
fetchAndCacheCitation(filename);
@@ -89,32 +91,32 @@ async function fetchAndCacheCitation(filename) {
*/
function handleCiteClick(e) {
// Check if clicked element or its parent is a cite button
const citeButton = e.target.closest('.js-cite-clipboard');
const citeButton = e.target.closest(".js-cite-clipboard");
if (!citeButton) return;
e.preventDefault();
e.stopPropagation();
const filename = citeButton.getAttribute('data-filename');
const filename = citeButton.getAttribute("data-filename");
if (!filename) {
if (isDebugMode) {
console.error('No filename specified for citation');
console.error("No filename specified for citation");
}
showNotification('Citation file not found', 'error');
showNotification("Citation file not found", "error");
return;
}
// Check if citation is cached
const cachedCitation = citationCache.get(filename);
if (cachedCitation) {
// Citation is cached, copy immediately (synchronous for Safari)
copyToClipboardSync(cachedCitation).then(success => {
copyToClipboardSync(cachedCitation).then((success) => {
if (success) {
showNotification(i18n?.copied || 'Citation copied!', 'success');
showNotification(i18n?.copied || "Citation copied!", "success");
updateButtonText(citeButton);
} else {
showNotification('Failed to copy citation', 'error');
showNotification("Failed to copy citation", "error");
}
});
} else {
@@ -135,23 +137,26 @@ async function fetchAndCopyWithFallback(filename, button) {
// Try to copy (might fail in Safari due to lost user activation)
const success = await copyToClipboardSync(citation);
if (success) {
showNotification(i18n?.copied || 'Citation copied!', 'success');
showNotification(i18n?.copied || "Citation copied!", "success");
updateButtonText(button);
} else {
showNotification('Failed to copy citation', 'error');
showNotification("Failed to copy citation", "error");
}
} else {
showNotification('Failed to load citation', 'error');
showNotification("Failed to load citation", "error");
}
} catch (error) {
if (isDebugMode) {
console.error('Failed to copy citation:', error);
console.error("Failed to copy citation:", error);
}
// If it's a NotAllowedError, suggest hovering first
if (error.name === 'NotAllowedError') {
showNotification('Please hover over the button first, then click', 'info');
if (error.name === "NotAllowedError") {
showNotification(
"Please hover over the button first, then click",
"info",
);
} else {
showNotification('Failed to copy citation', 'error');
showNotification("Failed to copy citation", "error");
}
}
}
@@ -161,29 +166,29 @@ async function fetchAndCopyWithFallback(filename, button) {
* @param {HTMLElement} button - The cite button element
*/
function updateButtonText(button) {
const copiedText = i18n?.copied || 'Copied!';
const copiedText = i18n?.copied || "Copied!";
// Find text element to update (skip icon)
const textElement = button.querySelector('span');
const textElement = button.querySelector("span");
if (!textElement) {
if (isDebugMode) {
console.warn('Could not find text element in cite button');
console.warn("Could not find text element in cite button");
}
return;
}
const originalText = textElement.textContent;
textElement.textContent = copiedText;
// Add visual feedback
button.classList.add('opacity-70');
button.classList.add("opacity-70");
// Revert after 2 seconds
setTimeout(() => {
textElement.textContent = originalText;
button.classList.remove('opacity-70');
button.classList.remove("opacity-70");
}, 2000);
}
// Export functions for potential reuse
export { handleCiteClick, prefetchAllCitations };
export { handleCiteClick, prefetchAllCitations };
+36 -35
View File
@@ -3,10 +3,10 @@
* Provides cross-browser clipboard functionality
*/
import { hugoEnvironment } from '@params';
import { hugoEnvironment } from "@params";
// Debug mode based on environment
const isDebugMode = hugoEnvironment === 'development';
const isDebugMode = hugoEnvironment === "development";
/**
* Copy text to clipboard with multiple fallback methods
@@ -19,16 +19,16 @@ export async function copyToClipboard(text) {
try {
await navigator.clipboard.writeText(text);
if (isDebugMode) {
console.log('Copied using Clipboard API');
console.log("Copied using Clipboard API");
}
return true;
} catch (err) {
if (isDebugMode) {
console.warn('Clipboard API failed:', err);
console.warn("Clipboard API failed:", err);
}
}
}
// Method 2: execCommand fallback
return copyUsingExecCommand(text);
}
@@ -42,16 +42,17 @@ export function copyToClipboardSync(text) {
return new Promise((resolve) => {
// Try modern clipboard API first
if (navigator.clipboard && window.isSecureContext) {
navigator.clipboard.writeText(text)
navigator.clipboard
.writeText(text)
.then(() => {
if (isDebugMode) {
console.log('Copied using Clipboard API (sync)');
console.log("Copied using Clipboard API (sync)");
}
resolve(true);
})
.catch(err => {
.catch((err) => {
if (isDebugMode) {
console.warn('Clipboard API failed:', err);
console.warn("Clipboard API failed:", err);
}
// Fallback to execCommand
resolve(copyUsingExecCommand(text));
@@ -69,30 +70,30 @@ export function copyToClipboardSync(text) {
* @returns {boolean} - Success status
*/
export function copyUsingExecCommand(text) {
const textarea = document.createElement('textarea');
const textarea = document.createElement("textarea");
textarea.value = text;
// Make it invisible but copyable
Object.assign(textarea.style, {
position: 'fixed',
top: '0',
left: '0',
width: '2em',
height: '2em',
padding: '0',
border: 'none',
outline: 'none',
boxShadow: 'none',
background: 'transparent',
fontSize: '16px', // Prevent zoom on iOS
position: "fixed",
top: "0",
left: "0",
width: "2em",
height: "2em",
padding: "0",
border: "none",
outline: "none",
boxShadow: "none",
background: "transparent",
fontSize: "16px", // Prevent zoom on iOS
});
document.body.appendChild(textarea);
// Select the text
textarea.focus();
textarea.select();
// For iOS
if (navigator.userAgent.match(/ipad|iphone/i)) {
const range = document.createRange();
@@ -102,19 +103,19 @@ export function copyUsingExecCommand(text) {
selection.addRange(range);
textarea.setSelectionRange(0, 999999);
}
let success = false;
try {
success = document.execCommand('copy');
success = document.execCommand("copy");
if (isDebugMode) {
console.log(`execCommand copy ${success ? 'succeeded' : 'failed'}`);
console.log(`execCommand copy ${success ? "succeeded" : "failed"}`);
}
} catch (err) {
if (isDebugMode) {
console.error('execCommand failed:', err);
console.error("execCommand failed:", err);
}
}
document.body.removeChild(textarea);
return success;
}
@@ -135,7 +136,7 @@ export class ClipboardCache {
constructor() {
this.cache = new Map();
}
/**
* Add content to cache
* @param {string} key - Cache key
@@ -147,7 +148,7 @@ export class ClipboardCache {
console.log(`Cached clipboard content for: ${key}`);
}
}
/**
* Get content from cache
* @param {string} key - Cache key
@@ -156,7 +157,7 @@ export class ClipboardCache {
get(key) {
return this.cache.get(key) || null;
}
/**
* Check if content is cached
* @param {string} key - Cache key
@@ -165,14 +166,14 @@ export class ClipboardCache {
has(key) {
return this.cache.has(key);
}
/**
* Clear the cache
*/
clear() {
this.cache.clear();
}
/**
* Get cache size
* @returns {number} - Number of cached items
+39 -33
View File
@@ -1,11 +1,11 @@
import { hugoEnvironment, i18n } from '@params';
import { hugoEnvironment, i18n } from "@params";
// Constants
const NOTIFICATION_DURATION = 2000; // milliseconds
const DEBOUNCE_DELAY = 300; // milliseconds
// Debug mode based on environment
const isDebugMode = hugoEnvironment === 'development';
const isDebugMode = hugoEnvironment === "development";
/**
* Debounce function to prevent rapid clicking
@@ -33,37 +33,37 @@ const debounce = (func, wait) => {
*/
async function copyCodeToClipboard(button, codeWrapper) {
if (!button || !(button instanceof HTMLElement)) {
throw new Error('Invalid button element');
throw new Error("Invalid button element");
}
if (!codeWrapper || !(codeWrapper instanceof HTMLElement)) {
throw new Error('Invalid code wrapper element');
throw new Error("Invalid code wrapper element");
}
// Clone the wrapper to avoid modifying the displayed content
const tempWrapper = codeWrapper.cloneNode(true);
// Remove the copy button from the cloned wrapper
const copyButton = tempWrapper.querySelector('.copy-button');
const copyButton = tempWrapper.querySelector(".copy-button");
if (copyButton) {
copyButton.remove();
}
const codeToCopy = tempWrapper.textContent?.trim() ?? '';
const codeToCopy = tempWrapper.textContent?.trim() ?? "";
if (!codeToCopy) {
throw new Error('No code content found to copy');
throw new Error("No code content found to copy");
}
try {
await navigator.clipboard.writeText(codeToCopy);
copiedNotification(button);
isDebugMode && console.debug('Code copied successfully');
isDebugMode && console.debug("Code copied successfully");
} catch (err) {
const errorMessage = err instanceof Error ? err.message : 'Unknown error';
console.error('Failed to copy:', errorMessage);
button.innerHTML = i18n['copyFailed'] || 'Failed';
const errorMessage = err instanceof Error ? err.message : "Unknown error";
console.error("Failed to copy:", errorMessage);
button.innerHTML = i18n["copyFailed"] || "Failed";
setTimeout(() => {
button.innerHTML = i18n['copy'];
button.innerHTML = i18n["copy"];
}, NOTIFICATION_DURATION);
throw err; // Re-throw for potential error boundary handling
}
@@ -74,14 +74,14 @@ async function copyCodeToClipboard(button, codeWrapper) {
* @param {HTMLElement} copyBtn - The copy button element
*/
function copiedNotification(copyBtn) {
copyBtn.innerHTML = i18n['copied'];
copyBtn.innerHTML = i18n["copied"];
copyBtn.disabled = true;
copyBtn.classList.add('copied');
copyBtn.classList.add("copied");
setTimeout(() => {
copyBtn.innerHTML = i18n['copy'];
copyBtn.innerHTML = i18n["copy"];
copyBtn.disabled = false;
copyBtn.classList.remove('copied');
copyBtn.classList.remove("copied");
}, NOTIFICATION_DURATION);
}
@@ -90,11 +90,14 @@ function copiedNotification(copyBtn) {
* @returns {HTMLButtonElement} The created button
*/
function createCopyButton() {
const copyBtn = document.createElement('button');
copyBtn.classList.add('copy-button');
copyBtn.innerHTML = i18n['copy'];
copyBtn.setAttribute('aria-label', i18n['copyLabel'] || 'Copy code to clipboard');
copyBtn.setAttribute('type', 'button'); // Explicit button type
const copyBtn = document.createElement("button");
copyBtn.classList.add("copy-button");
copyBtn.innerHTML = i18n["copy"];
copyBtn.setAttribute(
"aria-label",
i18n["copyLabel"] || "Copy code to clipboard",
);
copyBtn.setAttribute("type", "button"); // Explicit button type
return copyBtn;
}
@@ -106,25 +109,25 @@ function createCopyButton() {
function getCodeWrapper(codeblock) {
const container = codeblock.parentNode?.parentNode;
if (!container) {
throw new Error('Invalid code block structure');
throw new Error("Invalid code block structure");
}
if (container.classList.contains('highlight')) {
if (container.classList.contains("highlight")) {
return container;
}
const tableWrapper = container.closest('table');
const tableWrapper = container.closest("table");
if (tableWrapper) {
return tableWrapper;
}
const preElement = codeblock.parentElement;
if (preElement) {
preElement.classList.add('highlight');
preElement.classList.add("highlight");
return preElement;
}
throw new Error('Could not determine code wrapper');
throw new Error("Could not determine code wrapper");
}
/**
@@ -132,7 +135,7 @@ function getCodeWrapper(codeblock) {
*/
function initializeCodeCopyButtons() {
try {
const codeBlocks = document.querySelectorAll('pre > code');
const codeBlocks = document.querySelectorAll("pre > code");
isDebugMode && console.debug(`Found ${codeBlocks.length} code blocks`);
codeBlocks.forEach((codeblock, index) => {
@@ -143,23 +146,26 @@ function initializeCodeCopyButtons() {
// Use debounced version of copy function
const debouncedCopy = debounce(
() => copyCodeToClipboard(copyBtn, wrapper),
DEBOUNCE_DELAY
DEBOUNCE_DELAY,
);
copyBtn.addEventListener('click', debouncedCopy);
copyBtn.addEventListener("click", debouncedCopy);
wrapper.appendChild(copyBtn);
} catch (err) {
console.error(`Failed to initialize copy button for code block ${index}:`, err);
console.error(
`Failed to initialize copy button for code block ${index}:`,
err,
);
}
});
} catch (err) {
console.error('Failed to initialize code copy buttons:', err);
console.error("Failed to initialize code copy buttons:", err);
}
}
// Initialize when DOM is ready
if (document.readyState === 'loading') {
window.addEventListener('DOMContentLoaded', initializeCodeCopyButtons);
if (document.readyState === "loading") {
window.addEventListener("DOMContentLoaded", initializeCodeCopyButtons);
} else {
initializeCodeCopyButtons();
}
+6 -6
View File
@@ -1,19 +1,19 @@
import {initTheme, applyHugoStyleFixes} from './hb-init.js';
import { applyHugoStyleFixes, initTheme } from "./hb-init.js";
// Initialize Hugo Blox Builder global object
const root = document.documentElement;
const defaultTheme = root.dataset.wcThemeDefault || 'system';
const defaultTheme = root.dataset.wcThemeDefault || "system";
// Create global HBB object with theme functions
window.hbb = {
defaultTheme: defaultTheme,
setDarkTheme: function () {
root.classList.add('dark');
root.style.colorScheme = 'dark';
root.classList.add("dark");
root.style.colorScheme = "dark";
},
setLightTheme: function () {
root.classList.remove('dark');
root.style.colorScheme = 'light';
root.classList.remove("dark");
root.style.colorScheme = "light";
},
};
+11 -8
View File
@@ -1,12 +1,15 @@
window.addEventListener("DOMContentLoaded", () => {
// Toggle language chooser sub-menu
const languageChoosers = document.querySelectorAll('[data-hb-language-chooser]');
const languageChoosers = document.querySelectorAll(
"[data-hb-language-chooser]",
);
languageChoosers.forEach((languageChooser) => {
languageChooser.addEventListener('click', (e) => {
languageChooser.addEventListener("click", (e) => {
e.preventDefault();
languageChooser.dataset.state = languageChooser.dataset.state === 'open' ? 'closed' : 'open';
languageChooser.dataset.state =
languageChooser.dataset.state === "open" ? "closed" : "open";
const languageOptions = languageChooser.nextElementSibling;
languageOptions.classList.toggle('hidden');
languageOptions.classList.toggle("hidden");
const languageChooserRect = languageChooser.getBoundingClientRect();
const translateY = languageChooserRect.bottom - window.innerHeight + 40;
languageOptions.style.transform = `translate3d(${languageChooserRect.left}px, ${translateY}px, 0)`;
@@ -15,12 +18,12 @@ window.addEventListener("DOMContentLoaded", () => {
});
// Handle clicks outside chooser
document.addEventListener('click', (e) => {
if (e.target.closest('[data-hb-language-chooser]') === null) {
document.addEventListener("click", (e) => {
if (e.target.closest("[data-hb-language-chooser]") === null) {
languageChoosers.forEach((languageChooser) => {
languageChooser.dataset.state = 'closed';
languageChooser.dataset.state = "closed";
const languageOptions = languageChooser.nextElementSibling;
languageOptions.classList.add('hidden');
languageOptions.classList.add("hidden");
});
}
});
+23 -16
View File
@@ -3,40 +3,47 @@ export function initTheme() {
const root = document.documentElement;
const defaultTheme = root.dataset.wcThemeDefault;
const setDark = () => {
root.classList.add('dark');
root.style.colorScheme = 'dark';
root.classList.add("dark");
root.style.colorScheme = "dark";
};
const setLight = () => {
root.classList.remove('dark');
root.style.colorScheme = 'light';
root.classList.remove("dark");
root.style.colorScheme = "light";
};
if ('wc-color-theme' in localStorage) {
localStorage.getItem('wc-color-theme') === 'dark' ? setDark() : setLight();
if ("wc-color-theme" in localStorage) {
localStorage.getItem("wc-color-theme") === "dark" ? setDark() : setLight();
} else {
if (defaultTheme === 'dark') setDark();
else if (defaultTheme === 'light') setLight();
else if (defaultTheme === 'system') {
window.matchMedia('(prefers-color-scheme: dark)').matches ? setDark() : setLight();
if (defaultTheme === "dark") setDark();
else if (defaultTheme === "light") setLight();
else if (defaultTheme === "system") {
window.matchMedia("(prefers-color-scheme: dark)").matches
? setDark()
: setLight();
}
}
}
export function applyHugoStyleFixes() {
document.addEventListener('DOMContentLoaded', function () {
const checkboxes = document.querySelectorAll("li input[type='checkbox'][disabled]");
document.addEventListener("DOMContentLoaded", function () {
const checkboxes = document.querySelectorAll(
"li input[type='checkbox'][disabled]",
);
checkboxes.forEach((e) => {
const parent = e.parentElement?.parentElement;
if (parent) parent.classList.add('task-list');
if (parent) parent.classList.add("task-list");
});
const liNodes = document.querySelectorAll('.task-list li');
const liNodes = document.querySelectorAll(".task-list li");
liNodes.forEach((nodes) => {
const textNodes = Array.from(nodes.childNodes).filter(
(node) => node.nodeType === 3 && node.textContent && node.textContent.trim().length > 1,
(node) =>
node.nodeType === 3 &&
node.textContent &&
node.textContent.trim().length > 1,
);
if (textNodes.length > 0) {
const span = document.createElement('label');
const span = document.createElement("label");
textNodes[0].after(span);
const input = nodes.querySelector("input[type='checkbox']");
if (input) span.appendChild(input);
@@ -1,20 +1,24 @@
function getCssVar(name) {
const value = getComputedStyle(document.documentElement).getPropertyValue(name).trim();
return value ? 'rgb(' + value + ')' : null;
const value = getComputedStyle(document.documentElement)
.getPropertyValue(name)
.trim();
return value ? "rgb(" + value + ")" : null;
}
window.mermaid.initialize({
theme: 'base',
theme: "base",
themeVariables: {
background: getCssVar('--color-neutral-50') || '#fafafa',
primaryColor: getCssVar('--color-primary-200') || '#bfdbfe',
secondaryColor: getCssVar('--color-secondary-200') || '#a5f3fc',
tertiaryColor: getCssVar('--color-neutral-100') || '#f5f5f5',
primaryBorderColor: getCssVar('--color-primary-400') || '#60a5fa',
secondaryBorderColor: getCssVar('--color-secondary-400') || '#22d3ee',
tertiaryBorderColor: getCssVar('--color-neutral-400') || '#a3a3a3',
lineColor: getCssVar('--color-neutral-600') || '#525252',
fontFamily: getComputedStyle(document.documentElement).getPropertyValue('font-family'),
fontSize: '16px',
background: getCssVar("--color-neutral-50") || "#fafafa",
primaryColor: getCssVar("--color-primary-200") || "#bfdbfe",
secondaryColor: getCssVar("--color-secondary-200") || "#a5f3fc",
tertiaryColor: getCssVar("--color-neutral-100") || "#f5f5f5",
primaryBorderColor: getCssVar("--color-primary-400") || "#60a5fa",
secondaryBorderColor: getCssVar("--color-secondary-400") || "#22d3ee",
tertiaryBorderColor: getCssVar("--color-neutral-400") || "#a3a3a3",
lineColor: getCssVar("--color-neutral-600") || "#525252",
fontFamily: getComputedStyle(document.documentElement).getPropertyValue(
"font-family",
),
fontSize: "16px",
},
});
+6 -5
View File
@@ -1,11 +1,12 @@
// Navigation
const applyScrollPadding = () => {
const header = document.querySelector('.page-header');
const header = document.querySelector(".page-header");
let position = header.getBoundingClientRect();
document.documentElement.style.scrollPaddingTop = position.height.toString() + 'px';
const r = document.querySelector(':root');
r.style.setProperty('--navbar-height', position.height.toString() + 'px');
document.documentElement.style.scrollPaddingTop =
position.height.toString() + "px";
const r = document.querySelector(":root");
r.style.setProperty("--navbar-height", position.height.toString() + "px");
};
window.addEventListener("DOMContentLoaded", () => {
@@ -40,5 +41,5 @@ window.addEventListener("DOMContentLoaded", () => {
});
});
applyScrollPadding()
applyScrollPadding();
});
+53 -47
View File
@@ -3,10 +3,10 @@
* Provides reusable notification functionality
*/
import { hugoEnvironment } from '@params';
import { hugoEnvironment } from "@params";
// Debug mode based on environment
const isDebugMode = hugoEnvironment === 'development';
const isDebugMode = hugoEnvironment === "development";
/**
* Show a notification message using Tailwind styles
@@ -15,37 +15,40 @@ const isDebugMode = hugoEnvironment === 'development';
* @param {number} duration - Duration in milliseconds (default 3000)
* @returns {HTMLElement} - The notification element
*/
export function showNotification(message, type = 'success', duration = 3000) {
export function showNotification(message, type = "success", duration = 3000) {
// Get or create the notification container
const container = getOrCreateContainer();
// Remove any existing notifications with same message
const existingNotification = container.querySelector('.hb-notification');
if (existingNotification && existingNotification.textContent.includes(message)) {
const existingNotification = container.querySelector(".hb-notification");
if (
existingNotification &&
existingNotification.textContent.includes(message)
) {
existingNotification.remove();
}
// Create notification element
const notification = createNotificationElement(message, type);
// Add to container
container.appendChild(notification);
// Handle close button
const closeBtn = notification.querySelector('.hb-notification-close');
const closeBtn = notification.querySelector(".hb-notification-close");
if (closeBtn) {
closeBtn.addEventListener('click', () => {
closeBtn.addEventListener("click", () => {
removeNotification(notification);
});
}
// Auto-remove after duration
if (duration > 0) {
setTimeout(() => {
removeNotification(notification);
}, duration);
}
return notification;
}
@@ -54,20 +57,20 @@ export function showNotification(message, type = 'success', duration = 3000) {
* @returns {HTMLElement} - The container element
*/
function getOrCreateContainer() {
let container = document.getElementById('hb-notification-container');
let container = document.getElementById("hb-notification-container");
if (!container) {
if (isDebugMode) {
console.warn('Notification container not found, creating fallback');
console.warn("Notification container not found, creating fallback");
}
container = document.createElement('div');
container.id = 'hb-notification-container';
container.className = 'fixed top-20 right-4 z-[9999] pointer-events-none';
container.setAttribute('aria-live', 'polite');
container.setAttribute('aria-atomic', 'true');
container = document.createElement("div");
container.id = "hb-notification-container";
container.className = "fixed top-20 right-4 z-[9999] pointer-events-none";
container.setAttribute("aria-live", "polite");
container.setAttribute("aria-atomic", "true");
document.body.appendChild(container);
}
return container;
}
@@ -78,22 +81,22 @@ function getOrCreateContainer() {
* @returns {HTMLElement} - The notification element
*/
function createNotificationElement(message, type) {
const notification = document.createElement('div');
notification.setAttribute('role', 'alert');
const notification = document.createElement("div");
notification.setAttribute("role", "alert");
// Define colors for different types
const colors = {
success: { bg: '#10b981', bgClass: 'bg-green-500' },
error: { bg: '#ef4444', bgClass: 'bg-red-500' },
info: { bg: '#3b82f6', bgClass: 'bg-blue-500' },
warning: { bg: '#f59e0b', bgClass: 'bg-amber-500' }
success: { bg: "#10b981", bgClass: "bg-green-500" },
error: { bg: "#ef4444", bgClass: "bg-red-500" },
info: { bg: "#3b82f6", bgClass: "bg-blue-500" },
warning: { bg: "#f59e0b", bgClass: "bg-amber-500" },
};
const color = colors[type] || colors.info;
// Set classes
notification.className = `hb-notification pointer-events-auto flex items-center gap-2 px-4 py-3 text-white rounded-lg shadow-lg ${color.bgClass} animate-slide-in`;
// Add inline styles as fallback
notification.style.cssText = `
background-color: ${color.bg};
@@ -107,17 +110,20 @@ function createNotificationElement(message, type) {
margin-bottom: 0.5rem;
pointer-events: auto;
`;
// Add icon based on type
const icons = {
success: '<svg class="w-5 h-5 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path></svg>',
error: '<svg class="w-5 h-5 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"></path></svg>',
success:
'<svg class="w-5 h-5 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path></svg>',
error:
'<svg class="w-5 h-5 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"></path></svg>',
info: '<svg class="w-5 h-5 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd"></path></svg>',
warning: '<svg class="w-5 h-5 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clip-rule="evenodd"></path></svg>'
warning:
'<svg class="w-5 h-5 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clip-rule="evenodd"></path></svg>',
};
const icon = icons[type] || icons.info;
notification.innerHTML = `
${icon}
<span class="text-sm font-medium">${message}</span>
@@ -127,10 +133,10 @@ function createNotificationElement(message, type) {
</svg>
</button>
`;
// Ensure animation styles exist
ensureAnimationStyles();
return notification;
}
@@ -140,10 +146,10 @@ function createNotificationElement(message, type) {
*/
export function removeNotification(notification) {
if (!notification || !notification.parentNode) return;
notification.classList.remove('animate-slide-in');
notification.classList.add('animate-slide-out');
notification.classList.remove("animate-slide-in");
notification.classList.add("animate-slide-out");
setTimeout(() => {
if (notification.parentNode) {
notification.remove();
@@ -155,9 +161,9 @@ export function removeNotification(notification) {
* Ensure animation styles are in the document
*/
function ensureAnimationStyles() {
if (!document.querySelector('#hb-notification-styles')) {
const style = document.createElement('style');
style.id = 'hb-notification-styles';
if (!document.querySelector("#hb-notification-styles")) {
const style = document.createElement("style");
style.id = "hb-notification-styles";
style.textContent = `
@keyframes slide-in {
from {
+32 -23
View File
@@ -2,59 +2,64 @@
// Initialize search wrapper element globally
let searchWrapper = null;
window.addEventListener('DOMContentLoaded', () => {
const configScript = document.getElementById('search-config');
window.addEventListener("DOMContentLoaded", () => {
const configScript = document.getElementById("search-config");
if (!configScript) {
// No search on this page.
return;
}
const searchConfig = JSON.parse(configScript.textContent);
if (!searchConfig || typeof searchConfig.baseUrl === 'undefined') {
console.error('Pagefind search config is missing or invalid.', searchConfig);
if (!searchConfig || typeof searchConfig.baseUrl === "undefined") {
console.error(
"Pagefind search config is missing or invalid.",
searchConfig,
);
return;
}
try {
new PagefindUI({
element: '#search',
element: "#search",
showSubResults: true,
baseUrl: searchConfig.baseUrl,
bundlePath: searchConfig.baseUrl + 'pagefind/',
bundlePath: searchConfig.baseUrl + "pagefind/",
});
} catch (e) {
console.error('Failed to initialize PagefindUI:', e);
console.error("Failed to initialize PagefindUI:", e);
return;
}
// Get search wrapper element
searchWrapper = document.getElementById('search-wrapper');
searchWrapper = document.getElementById("search-wrapper");
// Add click handlers to triggers
let triggers = document.querySelectorAll('[data-search-toggle]');
triggers.forEach((trigger) => trigger.addEventListener('click', handleSearchToggle));
let triggers = document.querySelectorAll("[data-search-toggle]");
triggers.forEach((trigger) =>
trigger.addEventListener("click", handleSearchToggle),
);
});
function handleSearchToggle() {
if (!searchWrapper) return;
const isHidden = searchWrapper.classList.contains('hidden');
searchWrapper.classList.toggle('hidden');
document.body.style.overflow = isHidden ? 'hidden' : '';
const isHidden = searchWrapper.classList.contains("hidden");
searchWrapper.classList.toggle("hidden");
document.body.style.overflow = isHidden ? "hidden" : "";
const searchInput = searchWrapper.querySelector('input');
const searchInput = searchWrapper.querySelector("input");
if (searchInput) {
searchInput.value = '';
searchInput.value = "";
searchInput.focus();
}
if (!searchWrapper.classList.contains('hidden')) {
let clearTrigger = document.querySelector('.pagefind-ui__search-clear');
if (!searchWrapper.classList.contains("hidden")) {
let clearTrigger = document.querySelector(".pagefind-ui__search-clear");
if (clearTrigger && !clearTrigger.hasAttribute('listenerOnClick')) {
clearTrigger.setAttribute('listenerOnClick', 'true');
if (clearTrigger && !clearTrigger.hasAttribute("listenerOnClick")) {
clearTrigger.setAttribute("listenerOnClick", "true");
clearTrigger.addEventListener('click', () => {
clearTrigger.addEventListener("click", () => {
searchInput.focus();
});
}
@@ -62,8 +67,12 @@ function handleSearchToggle() {
}
// Add keyboard shortcut to close search modal with Escape key
document.addEventListener('keydown', (event) => {
if (event.key === 'Escape' && searchWrapper && !searchWrapper.classList.contains('hidden')) {
searchWrapper.classList.add('hidden');
document.addEventListener("keydown", (event) => {
if (
event.key === "Escape" &&
searchWrapper &&
!searchWrapper.classList.contains("hidden")
) {
searchWrapper.classList.add("hidden");
}
});
+27 -19
View File
@@ -5,25 +5,31 @@ document.addEventListener("DOMContentLoaded", function () {
e.preventDefault();
const parent = button.parentElement.parentElement;
if (parent) {
parent.classList.toggle("open")
parent.classList.toggle("open");
}
});
});
});
// Hamburger menu for mobile navigation
document.addEventListener('DOMContentLoaded', function () {
const menu = document.querySelector('#nav-toggle');
const overlay = document.querySelector('.hb-sidebar-mobile-menu');
const sidebarContainer = document.querySelector('.hb-sidebar-container');
document.addEventListener("DOMContentLoaded", function () {
const menu = document.querySelector("#nav-toggle");
const overlay = document.querySelector(".hb-sidebar-mobile-menu");
const sidebarContainer = document.querySelector(".hb-sidebar-container");
if (!overlay){
if (!overlay) {
return;
}
// Initialize the overlay
const overlayClasses = ['fixed', 'z-10', 'inset-0', 'bg-white', 'dark:bg-black/80'];
overlay.classList.add('bg-transparent');
const overlayClasses = [
"fixed",
"z-10",
"inset-0",
"bg-white",
"dark:bg-black/80",
];
overlay.classList.add("bg-transparent");
overlay.classList.remove("hidden", ...overlayClasses);
function toggleMenu() {
@@ -31,36 +37,38 @@ document.addEventListener('DOMContentLoaded', function () {
// See `hb-nav.js` - it handles the default behavior (irrespective of if sidebar is shown).
// When menu is open, show the navigation sidebar
sidebarContainer.classList.toggle('max-lg:[transform:translate3d(0,-100%,0)]');
sidebarContainer.classList.toggle('max-lg:[transform:translate3d(0,0,0)]');
sidebarContainer.classList.toggle(
"max-lg:[transform:translate3d(0,-100%,0)]",
);
sidebarContainer.classList.toggle("max-lg:[transform:translate3d(0,0,0)]");
// When menu is open, prevent body from scrolling
document.body.classList.toggle('overflow-hidden');
document.body.classList.toggle('lg:overflow-auto');
document.body.classList.toggle("overflow-hidden");
document.body.classList.toggle("lg:overflow-auto");
}
menu.addEventListener('click', (e) => {
console.debug("Hamburger clicked.");
menu.addEventListener("click", (e) => {
console.debug("Hamburger clicked.");
e.preventDefault();
toggleMenu();
if (overlay.classList.contains('bg-transparent')) {
if (overlay.classList.contains("bg-transparent")) {
// Show overlay
overlay.classList.add(...overlayClasses);
overlay.classList.remove('bg-transparent');
overlay.classList.remove("bg-transparent");
} else {
// Hide overlay
overlay.classList.remove(...overlayClasses);
overlay.classList.add('bg-transparent');
overlay.classList.add("bg-transparent");
}
});
overlay.addEventListener('click', (e) => {
overlay.addEventListener("click", (e) => {
e.preventDefault();
toggleMenu();
// Hide overlay
overlay.classList.remove(...overlayClasses);
overlay.classList.add('bg-transparent');
overlay.classList.add("bg-transparent");
});
});
+31 -15
View File
@@ -1,5 +1,5 @@
// Hugo Blox Builder Light / Dark theme toggle
document.addEventListener('DOMContentLoaded', function () {
document.addEventListener("DOMContentLoaded", function () {
addThemeToggleListener();
});
@@ -11,17 +11,23 @@ function addThemeToggleListener() {
if (
localStorage.getItem("wc-color-theme") === "dark" ||
(!("wc-color-theme" in localStorage) &&
((window.matchMedia("(prefers-color-scheme: dark)").matches && defaultTheme === "system") || defaultTheme === "dark"))
((window.matchMedia("(prefers-color-scheme: dark)").matches &&
defaultTheme === "system") ||
defaultTheme === "dark"))
) {
themeToggleButtons.forEach((el) => el.dataset.theme = "dark");
themeToggleButtons.forEach((el) => {
el.dataset.theme = "dark";
});
} else {
themeToggleButtons.forEach((el) => el.dataset.theme = "light");
themeToggleButtons.forEach((el) => {
el.dataset.theme = "light";
});
}
// Add click event handler to the light/dark buttons
themeToggleButtons.forEach((el) => {
el.addEventListener("click", function () {
console.debug('Theme toggled');
console.debug("Theme toggled");
if (localStorage.getItem("wc-color-theme")) {
if (localStorage.getItem("wc-color-theme") === "light") {
window.hbb.setDarkTheme();
@@ -39,21 +45,31 @@ function addThemeToggleListener() {
localStorage.setItem("wc-color-theme", "dark");
}
}
el.dataset.theme = document.documentElement.classList.contains("dark") ? "dark" : "light";
el.dataset.theme = document.documentElement.classList.contains("dark")
? "dark"
: "light";
// Dispatch `hbThemeChange` event to support themeable user plugins.
const themeChangeEvent = new CustomEvent('hbThemeChange', {detail: {isDarkTheme: () => document.documentElement.classList.contains("dark")}});
const themeChangeEvent = new CustomEvent("hbThemeChange", {
detail: {
isDarkTheme: () =>
document.documentElement.classList.contains("dark"),
},
});
document.dispatchEvent(themeChangeEvent);
});
});
// Listen for dark mode toggling in OS
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", (event) => {
if (defaultTheme === "system" && !("wc-color-theme" in localStorage)) {
event.matches ? window.hbb.setDarkTheme() : window.hbb.setLightTheme();
themeToggleButtons.forEach((el) =>
el.dataset.theme = document.documentElement.classList.contains("dark") ? "dark" : "light"
);
}
});
window
.matchMedia("(prefers-color-scheme: dark)")
.addEventListener("change", (event) => {
if (defaultTheme === "system" && !("wc-color-theme" in localStorage)) {
event.matches ? window.hbb.setDarkTheme() : window.hbb.setLightTheme();
themeToggleButtons.forEach((el) => {
const isDark = document.documentElement.classList.contains("dark");
el.dataset.theme = isDark ? "dark" : "light";
});
}
});
}
@@ -1,5 +1,2 @@
// Dummy entry for Vite lib mode. We copy vendor libraries via vite-plugin-static-copy.
export default {}
export default {};
@@ -4,11 +4,25 @@
"version": "1.0.0",
"license": "MIT",
"category": "content",
"tags": ["collection", "blog", "publications", "filter", "sort", "content-display", "archive"],
"tags": [
"collection",
"blog",
"publications",
"filter",
"sort",
"content-display",
"archive"
],
"description": "Display filtered and sorted collections of pages with customizable views and pagination",
"author": "Hugo Blox",
"homepage": "https://hugoblox.com/blocks/",
"repository": "https://github.com/HugoBlox/hugo-blox-builder",
"keywords": ["hugo", "static-site", "content", "blog", "publications", "filtering"]
"keywords": [
"hugo",
"static-site",
"content",
"blog",
"publications",
"filtering"
]
}
@@ -115,4 +115,3 @@
}
]
}
@@ -4,11 +4,25 @@
"version": "1.0.0",
"license": "MIT",
"category": "marketing",
"tags": ["cta", "buttons", "links", "social", "marketing", "conversion", "list"],
"tags": [
"cta",
"buttons",
"links",
"social",
"marketing",
"conversion",
"list"
],
"description": "Create compelling vertical lists of call-to-action buttons with icons and hover effects",
"author": "Hugo Blox",
"homepage": "https://hugoblox.com/blocks/",
"repository": "https://github.com/HugoBlox/hugo-blox-builder",
"keywords": ["hugo", "cta", "buttons", "conversion", "marketing", "social-links"]
"keywords": [
"hugo",
"cta",
"buttons",
"conversion",
"marketing",
"social-links"
]
}
@@ -46,4 +46,3 @@
}
]
}
@@ -4,11 +4,18 @@
"version": "1.0.0",
"license": "MIT",
"category": "marketing",
"tags": ["cta", "card", "conversion", "marketing", "hero", "call-to-action", "banner"],
"tags": [
"cta",
"card",
"conversion",
"marketing",
"hero",
"call-to-action",
"banner"
],
"description": "Create high-impact call-to-action cards with bold typography and compelling buttons",
"author": "Hugo Blox",
"homepage": "https://hugoblox.com/blocks/",
"repository": "https://github.com/HugoBlox/hugo-blox-builder",
"keywords": ["hugo", "cta", "marketing", "conversion", "banner", "card"]
}
@@ -4,11 +4,25 @@
"version": "1.0.0",
"license": "MIT",
"category": "marketing",
"tags": ["cta", "image", "features", "alternating", "marketing", "storytelling", "visual"],
"tags": [
"cta",
"image",
"features",
"alternating",
"marketing",
"storytelling",
"visual"
],
"description": "Engaging alternating image and text sections with feature lists and call-to-action buttons",
"author": "Hugo Blox",
"homepage": "https://hugoblox.com/blocks/",
"repository": "https://github.com/HugoBlox/hugo-blox-builder",
"keywords": ["hugo", "marketing", "images", "features", "storytelling", "conversion"]
"keywords": [
"hugo",
"marketing",
"images",
"features",
"storytelling",
"conversion"
]
}
@@ -73,4 +73,3 @@
}
]
}
@@ -4,11 +4,18 @@
"version": "1.0.0",
"license": "MIT",
"category": "content",
"tags": ["features", "grid", "icons", "benefits", "product", "services", "highlights"],
"tags": [
"features",
"grid",
"icons",
"benefits",
"product",
"services",
"highlights"
],
"description": "Showcase features, benefits, or services in an organized grid with icons and descriptions",
"author": "Hugo Blox",
"homepage": "https://hugoblox.com/blocks/",
"repository": "https://github.com/HugoBlox/hugo-blox-builder",
"keywords": ["hugo", "features", "grid", "services", "benefits", "product"]
}
@@ -54,4 +54,3 @@
}
]
}
@@ -4,11 +4,18 @@
"version": "1.0.0",
"license": "MIT",
"category": "marketing",
"tags": ["hero", "landing", "cta", "announcement", "headline", "conversion", "primary"],
"tags": [
"hero",
"landing",
"cta",
"announcement",
"headline",
"conversion",
"primary"
],
"description": "Create compelling hero sections with headlines, announcements, and dual call-to-action buttons",
"author": "Hugo Blox",
"homepage": "https://hugoblox.com/blocks/",
"repository": "https://github.com/HugoBlox/hugo-blox-builder",
"keywords": ["hugo", "hero", "landing-page", "cta", "marketing", "headlines"]
}
@@ -91,4 +91,3 @@
}
]
}
@@ -4,11 +4,18 @@
"version": "1.0.0",
"license": "MIT",
"category": "content",
"tags": ["markdown", "content", "text", "prose", "writing", "simple", "flexible"],
"tags": [
"markdown",
"content",
"text",
"prose",
"writing",
"simple",
"flexible"
],
"description": "Display rich markdown content with beautiful typography and optional section titles",
"author": "Hugo Blox",
"homepage": "https://hugoblox.com/blocks/",
"repository": "https://github.com/HugoBlox/hugo-blox-builder",
"keywords": ["hugo", "markdown", "content", "typography", "prose", "text"]
}
@@ -32,4 +32,3 @@
}
]
}
@@ -4,11 +4,25 @@
"version": "1.0.0",
"license": "MIT",
"category": "resume",
"tags": ["resume", "awards", "certifications", "achievements", "credentials", "professional", "cv"],
"tags": [
"resume",
"awards",
"certifications",
"achievements",
"credentials",
"professional",
"cv"
],
"description": "Showcase professional awards and certifications with elegant card layouts and certificate links",
"author": "Hugo Blox",
"homepage": "https://hugoblox.com/blocks/",
"repository": "https://github.com/HugoBlox/hugo-blox-builder",
"keywords": ["hugo", "resume", "awards", "certifications", "cv", "professional"]
"keywords": [
"hugo",
"resume",
"awards",
"certifications",
"cv",
"professional"
]
}
@@ -53,4 +53,3 @@
}
]
}
@@ -4,11 +4,27 @@
"version": "1.0.0",
"license": "MIT",
"category": "resume",
"tags": ["resume", "biography", "profile", "horizontal", "education", "interests", "cv", "layout"],
"tags": [
"resume",
"biography",
"profile",
"horizontal",
"education",
"interests",
"cv",
"layout"
],
"description": "Horizontal biography layout with integrated education and interests sections",
"author": "Hugo Blox",
"homepage": "https://hugoblox.com/blocks/",
"repository": "https://github.com/HugoBlox/hugo-blox-builder",
"keywords": ["hugo", "resume", "biography", "horizontal", "education", "interests", "cv"]
"keywords": [
"hugo",
"resume",
"biography",
"horizontal",
"education",
"interests",
"cv"
]
}
@@ -4,11 +4,27 @@
"version": "1.0.0",
"license": "MIT",
"category": "resume",
"tags": ["resume", "biography", "profile", "avatar", "social", "about", "personal", "cv"],
"tags": [
"resume",
"biography",
"profile",
"avatar",
"social",
"about",
"personal",
"cv"
],
"description": "Professional biography section with customizable avatar, social links, and personal information",
"author": "Hugo Blox",
"homepage": "https://hugoblox.com/blocks/",
"repository": "https://github.com/HugoBlox/hugo-blox-builder",
"keywords": ["hugo", "resume", "biography", "profile", "avatar", "social", "cv"]
"keywords": [
"hugo",
"resume",
"biography",
"profile",
"avatar",
"social",
"cv"
]
}
@@ -97,4 +97,3 @@
}
]
}
@@ -4,11 +4,27 @@
"version": "1.0.0",
"license": "MIT",
"category": "resume",
"tags": ["resume", "experience", "timeline", "work", "education", "career", "professional", "cv"],
"tags": [
"resume",
"experience",
"timeline",
"work",
"education",
"career",
"professional",
"cv"
],
"description": "Professional timeline displaying work experience and education history in chronological order",
"author": "Hugo Blox",
"homepage": "https://hugoblox.com/blocks/",
"repository": "https://github.com/HugoBlox/hugo-blox-builder",
"keywords": ["hugo", "resume", "experience", "timeline", "work", "education", "career"]
"keywords": [
"hugo",
"resume",
"experience",
"timeline",
"work",
"education",
"career"
]
}
@@ -54,4 +54,3 @@
}
]
}
@@ -4,11 +4,27 @@
"version": "1.0.0",
"license": "MIT",
"category": "resume",
"tags": ["resume", "languages", "multilingual", "skills", "fluency", "international", "cv", "circular-progress"],
"tags": [
"resume",
"languages",
"multilingual",
"skills",
"fluency",
"international",
"cv",
"circular-progress"
],
"description": "Showcase language proficiency with beautiful circular progress indicators and percentage displays",
"author": "Hugo Blox",
"homepage": "https://hugoblox.com/blocks/",
"repository": "https://github.com/HugoBlox/hugo-blox-builder",
"keywords": ["hugo", "resume", "languages", "multilingual", "fluency", "skills", "cv"]
"keywords": [
"hugo",
"resume",
"languages",
"multilingual",
"fluency",
"skills",
"cv"
]
}
@@ -36,4 +36,3 @@
}
]
}
@@ -4,11 +4,27 @@
"version": "1.0.0",
"license": "MIT",
"category": "resume",
"tags": ["resume", "skills", "technical", "progress-bars", "expertise", "professional", "competencies", "cv"],
"tags": [
"resume",
"skills",
"technical",
"progress-bars",
"expertise",
"professional",
"competencies",
"cv"
],
"description": "Display technical and professional skills with optional progress bars and organized categories",
"author": "Hugo Blox",
"homepage": "https://hugoblox.com/blocks/",
"repository": "https://github.com/HugoBlox/hugo-blox-builder",
"keywords": ["hugo", "resume", "skills", "technical", "progress", "expertise", "cv"]
"keywords": [
"hugo",
"resume",
"skills",
"technical",
"progress",
"expertise",
"cv"
]
}
@@ -53,4 +53,3 @@
}
]
}
@@ -181,4 +181,3 @@
"required": ["block"],
"additionalProperties": true
}
@@ -100,4 +100,3 @@
"required": ["type", "sections"],
"additionalProperties": true
}
@@ -6,22 +6,21 @@
"type": "array",
"items": {
"oneOf": [
{"$ref": "../hero/schema.json"},
{"$ref": "../features/schema.json"},
{"$ref": "../collection/schema.json"},
{"$ref": "../markdown/schema.json"},
{"$ref": "../stats/schema.json"},
{"$ref": "../testimonials/schema.json"},
{"$ref": "../cta-button-list/schema.json"},
{"$ref": "../cta-card/schema.json"},
{"$ref": "../cta-image-paragraph/schema.json"},
{"$ref": "../resume-experience/schema.json"},
{"$ref": "../resume-biography/schema.json"},
{"$ref": "../resume-biography-3/schema.json"},
{"$ref": "../resume-skills/schema.json"},
{"$ref": "../resume-awards/schema.json"},
{"$ref": "../resume-languages/schema.json"}
{ "$ref": "../hero/schema.json" },
{ "$ref": "../features/schema.json" },
{ "$ref": "../collection/schema.json" },
{ "$ref": "../markdown/schema.json" },
{ "$ref": "../stats/schema.json" },
{ "$ref": "../testimonials/schema.json" },
{ "$ref": "../cta-button-list/schema.json" },
{ "$ref": "../cta-card/schema.json" },
{ "$ref": "../cta-image-paragraph/schema.json" },
{ "$ref": "../resume-experience/schema.json" },
{ "$ref": "../resume-biography/schema.json" },
{ "$ref": "../resume-biography-3/schema.json" },
{ "$ref": "../resume-skills/schema.json" },
{ "$ref": "../resume-awards/schema.json" },
{ "$ref": "../resume-languages/schema.json" }
]
}
}
+17 -3
View File
@@ -4,11 +4,25 @@
"version": "1.0.0",
"license": "MIT",
"category": "content",
"tags": ["statistics", "numbers", "metrics", "achievements", "data", "showcase", "grid"],
"tags": [
"statistics",
"numbers",
"metrics",
"achievements",
"data",
"showcase",
"grid"
],
"description": "Showcase impressive statistics and achievements in a clean, impactful grid layout",
"author": "Hugo Blox",
"homepage": "https://hugoblox.com/blocks/",
"repository": "https://github.com/HugoBlox/hugo-blox-builder",
"keywords": ["hugo", "statistics", "metrics", "achievements", "data", "numbers"]
"keywords": [
"hugo",
"statistics",
"metrics",
"achievements",
"data",
"numbers"
]
}
@@ -50,4 +50,3 @@
}
]
}
@@ -4,11 +4,25 @@
"version": "1.0.0",
"license": "MIT",
"category": "marketing",
"tags": ["testimonials", "reviews", "social-proof", "quotes", "customers", "trust", "credibility"],
"tags": [
"testimonials",
"reviews",
"social-proof",
"quotes",
"customers",
"trust",
"credibility"
],
"description": "Build trust with authentic customer testimonials featuring quotes, names, and profile photos",
"author": "Hugo Blox",
"homepage": "https://hugoblox.com/blocks/",
"repository": "https://github.com/HugoBlox/hugo-blox-builder",
"keywords": ["hugo", "testimonials", "social-proof", "reviews", "customers", "trust"]
"keywords": [
"hugo",
"testimonials",
"social-proof",
"reviews",
"customers",
"trust"
]
}
@@ -58,4 +58,3 @@
}
]
}
File diff suppressed because it is too large. Load diff
+2 -9
View File
@@ -13,15 +13,8 @@
"spdx": "MIT",
"url": "https://github.com/tailwindlabs/heroicons/blob/master/LICENSE"
},
"samples": [
"camera",
"building-library",
"receipt-refund"
],
"height": [
24,
20
],
"samples": ["camera", "building-library", "receipt-refund"],
"height": [24, 20],
"category": "General",
"palette": false
},