Skip to content

Commit 119a074

Browse files
committed
feat: implement dynamic Prism theme switching and add atom-dark theme support
1 parent b965518 commit 119a074

File tree

4 files changed

+203
-20
lines changed

4 files changed

+203
-20
lines changed

Web/Pages/Shared/_Layout.cshtml

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,8 @@
9393

9494
<link rel="preload" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
9595
<noscript><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css"></noscript>
96-
97-
<link rel="preload" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism.min.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
98-
<noscript><link id="prism-theme" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism.min.css"></noscript>
96+
<link id="prism-theme" rel="preload" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism.min.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
97+
<noscript><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism.min.css"></noscript>
9998

10099
<!-- Site CSS -->
101100
<environment exclude="Development">
@@ -205,34 +204,47 @@
205204
<script src="~/js/site.js" asp-append-version="true"></script>
206205
<script src="~/js/analytics-stub.js" asp-append-version="true"></script>
207206
<script src="~/js/theme-switcher.js" asp-append-version="true"></script>
208-
</environment>
209-
<script>
207+
</environment> <script>
210208
// Fix for code highlighting in theme changes
211209
document.addEventListener('DOMContentLoaded', function() {
212210
// Initial theme setup based on current setting
213211
const resolvedTheme = document.documentElement.getAttribute('data-theme-resolved');
214212
const prismThemeLink = document.getElementById('prism-theme');
215213
216214
if (prismThemeLink && resolvedTheme) {
217-
const themeUrl = resolvedTheme === 'dark'
218-
? 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-tomorrow.min.css'
215+
const themeUrl = resolvedTheme === 'dark'
216+
? '/css/prism-atom-dark.css'
219217
: 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism.min.css';
218+
219+
console.log(`Initial Prism theme setting to: ${resolvedTheme} (${themeUrl})`);
220220
prismThemeLink.href = themeUrl;
221+
} else if (!prismThemeLink) {
222+
console.warn('Prism theme link not found, creating element');
223+
const newLink = document.createElement('link');
224+
newLink.id = 'prism-theme';
225+
newLink.rel = 'stylesheet';
226+
newLink.href = resolvedTheme === 'dark'
227+
? '/css/prism-atom-dark.css'
228+
: 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism.min.css';
229+
document.head.appendChild(newLink);
221230
}
222231
223232
// Initial highlight
224233
if (window.Prism) {
225234
setTimeout(() => Prism.highlightAll(), 100);
226235
}
227-
228-
// Auto mode special case - ensure system preference changes are handled
236+
// Auto mode special case - ensure system preference changes are handled
229237
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', function(e) {
230238
if (document.documentElement.getAttribute('data-theme') === 'auto') {
231-
// Update Prism theme on system preference change
239+
// Update data-theme-resolved attribute
232240
const isDark = e.matches;
241+
document.documentElement.setAttribute('data-theme-resolved', isDark ? 'dark' : 'light');
242+
243+
// Update Prism theme on system preference change
244+
const prismThemeLink = document.getElementById('prism-theme');
233245
if (prismThemeLink) {
234246
prismThemeLink.href = isDark
235-
? 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-tomorrow.min.css'
247+
? '/css/prism-atom-dark.css'
236248
: 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism.min.css';
237249
238250
if (window.Prism) {

Web/Pages/Tips/Details.cshtml

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -392,9 +392,13 @@ document.addEventListener('DOMContentLoaded', function() {
392392
// Wait a bit to ensure all styles are loaded
393393
setTimeout(function() {
394394
if (typeof Prism !== 'undefined') {
395+
395396
// Remove any existing highlighting first
396397
document.querySelectorAll('pre[class*="language-"] code').forEach(function(codeBlock) {
397-
codeBlock.removeAttribute('class');
398+
if (codeBlock.hasAttribute('class')) {
399+
codeBlock.setAttribute('data-original-class', codeBlock.className);
400+
codeBlock.removeAttribute('class');
401+
}
398402
codeBlock.className = codeBlock.parentElement.className;
399403
});
400404
@@ -426,6 +430,17 @@ document.addEventListener('DOMContentLoaded', function() {
426430
}, 100);
427431
});
428432
433+
// Listen for theme changes and re-highlight code
434+
document.addEventListener('theme-changed', function(e) {
435+
// We need a small delay to ensure the CSS has been applied
436+
setTimeout(function() {
437+
if (window.Prism) {
438+
console.log('Re-highlighting code blocks after theme change to: ' + e.detail.resolvedTheme);
439+
Prism.highlightAll();
440+
}
441+
}, 200);
442+
});
443+
429444
document.addEventListener('DOMContentLoaded', function() {
430445
// Track tip view
431446
Analytics.trackTipView('@Model.ViewModel.Tip.Title', '@Model.ViewModel.Tip.Category');
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
/**
2+
* atom-dark theme for `prism.js`
3+
* Based on Atom's `atom-dark` theme: https://github.com/atom/atom-dark-syntax
4+
* @author Joe Gibson (@gibsjose)
5+
*/
6+
7+
code[class*="language-"],
8+
pre[class*="language-"] {
9+
color: #c5c8c6;
10+
text-shadow: 0 1px rgba(0, 0, 0, 0.3);
11+
font-family: Inconsolata, Monaco, Consolas, 'Courier New', Courier, monospace;
12+
direction: ltr;
13+
text-align: left;
14+
white-space: pre;
15+
word-spacing: normal;
16+
word-break: normal;
17+
line-height: 1.5;
18+
19+
-moz-tab-size: 4;
20+
-o-tab-size: 4;
21+
tab-size: 4;
22+
23+
-webkit-hyphens: none;
24+
-moz-hyphens: none;
25+
-ms-hyphens: none;
26+
hyphens: none;
27+
}
28+
29+
/* Code blocks */
30+
pre[class*="language-"] {
31+
padding: 1em;
32+
margin: .5em 0;
33+
overflow: auto;
34+
border-radius: 0.3em;
35+
}
36+
37+
:not(pre) > code[class*="language-"],
38+
pre[class*="language-"] {
39+
background: #1d1f21;
40+
}
41+
42+
/* Inline code */
43+
:not(pre) > code[class*="language-"] {
44+
padding: .1em;
45+
border-radius: .3em;
46+
}
47+
48+
.token.comment,
49+
.token.prolog,
50+
.token.doctype,
51+
.token.cdata {
52+
color: #7C7C7C;
53+
}
54+
55+
.token.punctuation {
56+
color: #c5c8c6;
57+
}
58+
59+
.namespace {
60+
opacity: .7;
61+
}
62+
63+
.token.property,
64+
.token.keyword,
65+
.token.tag {
66+
color: #96CBFE;
67+
}
68+
69+
.token.class-name {
70+
color: #FFFFB6;
71+
text-decoration: underline;
72+
}
73+
74+
.token.boolean,
75+
.token.constant {
76+
color: #99CC99;
77+
}
78+
79+
.token.symbol,
80+
.token.deleted {
81+
color: #f92672;
82+
}
83+
84+
.token.number {
85+
color: #FF73FD;
86+
}
87+
88+
.token.selector,
89+
.token.attr-name,
90+
.token.string,
91+
.token.char,
92+
.token.builtin,
93+
.token.inserted {
94+
color: #A8FF60;
95+
}
96+
97+
.token.variable {
98+
color: #C6C5FE;
99+
}
100+
101+
.token.operator {
102+
color: #EDEDED;
103+
}
104+
105+
.token.entity {
106+
color: #FFFFB6;
107+
cursor: help;
108+
}
109+
110+
.token.url {
111+
color: #96CBFE;
112+
}
113+
114+
.language-css .token.string,
115+
.style .token.string {
116+
color: #87C38A;
117+
}
118+
119+
.token.atrule,
120+
.token.attr-value {
121+
color: #F9EE98;
122+
}
123+
124+
.token.function {
125+
color: #DAD085;
126+
}
127+
128+
.token.regex {
129+
color: #E9C062;
130+
}
131+
132+
.token.important {
133+
color: #fd971f;
134+
}
135+
136+
.token.important,
137+
.token.bold {
138+
font-weight: bold;
139+
}
140+
141+
.token.italic {
142+
font-style: italic;
143+
}

Web/wwwroot/js/theme-switcher.js

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -135,21 +135,34 @@ class ThemeSwitcher {
135135
}));
136136
} // Load the appropriate Prism theme based on current theme
137137
updatePrismTheme(resolvedTheme) {
138-
const prismThemeLink = document.getElementById('prism-theme');
139-
if (prismThemeLink) {
140-
const themeUrl = resolvedTheme === 'dark'
141-
? 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-tomorrow.min.css'
142-
: 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism.min.css';
143-
144-
// Update theme URL and trigger rehighlight
138+
// Look for the existing Prism theme link
139+
let prismThemeLink = document.getElementById('prism-theme');
140+
141+
// If it doesn't exist, create it
142+
if (!prismThemeLink) {
143+
prismThemeLink = document.createElement('link');
144+
prismThemeLink.id = 'prism-theme';
145+
prismThemeLink.rel = 'stylesheet';
146+
document.head.appendChild(prismThemeLink);
147+
}
148+
149+
const themeUrl = resolvedTheme === 'dark'
150+
? '/css/prism-atom-dark.css'
151+
: 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism.min.css';
152+
153+
// Only update if the theme URL has changed
154+
if (prismThemeLink.href !== themeUrl) {
155+
// Update theme URL
145156
prismThemeLink.href = themeUrl;
146157

147158
// Re-highlight code if Prism is available
148159
if (window.Prism) {
149160
setTimeout(() => {
150161
Prism.highlightAll();
151-
}, 100); // Small delay to ensure theme is loaded
162+
}, 200); // Slightly longer delay to ensure theme is loaded
152163
}
164+
165+
console.log(`Prism theme updated to: ${resolvedTheme === 'dark' ? 'dark' : 'light'}`);
153166
}
154167
}
155168
updateNavbarStyling() {

0 commit comments

Comments
 (0)