Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add sugar, links, css & content to /everything page #55

Merged
merged 37 commits into from Mar 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
9f58d62
feat: add sugar, links, css & content to /everything page
blksnk Mar 14, 2024
4b24e4f
chore: remove starting hyphen in lesson ids
blksnk Mar 14, 2024
34f0e33
style: more responsive & useful ui
blksnk Mar 14, 2024
5cbc3ca
fix: revert common.css changes
blksnk Mar 14, 2024
ab4e129
fix(css): revert common off-white color
blksnk Mar 14, 2024
a4e77b9
feat(style): preserve style with original common colors
blksnk Mar 14, 2024
5f91cc2
style: tweak dim-bg class
blksnk Mar 14, 2024
ed914fd
style: mobile table of contents overlay
blksnk Mar 14, 2024
65dfa50
style: revert changes to playground & global navbar
blksnk Mar 14, 2024
8ec0235
fix: code snipped dimming
blksnk Mar 14, 2024
ad29a3c
style: replicate code bloc style, polish mobile table of contents
blksnk Mar 14, 2024
c0198d0
style: tweak light mode code block
blksnk Mar 14, 2024
334cecf
style: tweak colors, hovers & snipets
blksnk Mar 15, 2024
8d9a0ad
fix(style): remove empty ruleset
blksnk Mar 15, 2024
b5846e3
feat(wip): modular page rendering
blksnk Mar 15, 2024
7562575
feat(wip): add hljs for gleam
blksnk Mar 15, 2024
899c616
chore: move global components to their own module
blksnk Mar 16, 2024
06bf628
feat: atom one dark / light generic declaration
blksnk Mar 16, 2024
f113fa5
style: add support for more color schemes
blksnk Mar 16, 2024
15ef473
syntax highlight regexes
blksnk Mar 16, 2024
066fd73
chore: move highlight.js core
blksnk Mar 16, 2024
e70d979
feat: highlight js gleam lang definition
blksnk Mar 16, 2024
fe7061a
feat: add link to /everyting in nav
blksnk Mar 16, 2024
f6c9e30
chore: format css
blksnk Mar 16, 2024
528b30d
chore: format files
blksnk Mar 16, 2024
008336c
chore: move everything css to separate file
blksnk Mar 16, 2024
120fd82
feat: move everything page generation template to own module
blksnk Mar 16, 2024
6534534
chore: group functions into 3 modules
blksnk Mar 17, 2024
22c5f51
chore: deplicate function calls
blksnk Mar 17, 2024
3d43a9b
fix: FOUC on /everyting when on dark theme
blksnk Mar 17, 2024
769ea82
chore: delete render module
blksnk Mar 17, 2024
c5998a8
chore: seperate common page styles from lesson page styles
blksnk Mar 17, 2024
e28e98a
chore: use split css stylesheets
blksnk Mar 17, 2024
4482ee3
style: add breathing room between lesson paragraphs
blksnk Mar 17, 2024
22d4288
fix: snippet link margin
blksnk Mar 17, 2024
8cbbc90
style: tweak link color
blksnk Mar 17, 2024
38303a6
style: tweak light accent color for better accessibility
blksnk Mar 17, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
637 changes: 474 additions & 163 deletions src/tour.gleam

Large diffs are not rendered by default.

180 changes: 148 additions & 32 deletions src/tour/widgets.gleam
@@ -1,4 +1,5 @@
import htmb.{type Html, h}
import gleam/list
import htmb.{type Html, h, text}

pub fn icon_moon() -> Html {
h("svg", [#("id", "icon-moon"), #("viewBox", "0 0 24 24")], [
Expand Down Expand Up @@ -60,48 +61,163 @@ pub fn icon_toggle_right() -> Html {
])
}

pub fn theme_picker() -> Html {
h("div", [#("class", "theme-picker")], [
h(
"button",
[
#("type", "button"),
#("alt", "Switch to light mode"),
#("title", "Switch to light mode"),
#("class", "theme-button -light"),
#("data-light-theme-toggle", ""),
],
[icon_moon(), icon_toggle_left()],
),
h(
"button",
[
#("type", "button"),
#("alt", "Switch to dark mode"),
#("title", "Switch to dark mode"),
#("class", "theme-button -dark"),
#("data-dark-theme-toggle", ""),
],
[icon_sun(), icon_toggle_right()],
),
])
}

// This script is inlined in the response to avoid FOUC when applying the theme
pub const theme_picker_js = "
const mediaPrefersDarkTheme = window.matchMedia('(prefers-color-scheme: dark)')
const mediaPrefersDarkTheme = window.matchMedia('(prefers-color-scheme: dark)');
const themeStorageKey = 'theme';

function selectTheme(selectedTheme) {
// Apply and remember the specified theme.
applyTheme(selectedTheme)
if ((selectedTheme === 'dark') === mediaPrefersDarkTheme.matches) {
function getPreferredTheme() {
return mediaPrefersDarkTheme.matches ? 'dark' : 'light';
}

function getAppliedTheme() {
return document.documentElement.classList.contains('theme-dark')
? 'dark'
: 'light';
}

function getStoredTheme() {
return localStorage.getItem(themeStorageKey);
}

function storeTheme(selectedTheme) {
localStorage.setItem(themeStorageKey, selectedTheme);
}

function syncStoredTheme(theme) {
if (theme === getPreferredTheme()) {
// Selected theme is the same as the device's preferred theme, so we can forget this setting.
localStorage.removeItem('theme')
localStorage.removeItem(themeStorageKey);
} else {
// Remember the selected theme to apply it on the next visit
localStorage.setItem('theme', selectedTheme)
storeTheme(theme);
}
}

function applyTheme(theme) {
document.documentElement.classList.toggle('theme-dark', theme === 'dark')
document.documentElement.classList.toggle('theme-light', theme !== 'dark')
function applyTheme(theme, initial = false) {
// abort if theme is already applied
if (theme === getAppliedTheme()) return;
// apply theme css class
document.documentElement.classList.toggle('theme-dark', theme === 'dark');
document.documentElement.classList.toggle('theme-light', theme !== 'dark');
}

// If user had selected a theme, load it. Otherwise, use device's preferred theme
const selectedTheme = localStorage.getItem('theme')
if (selectedTheme) {
applyTheme(selectedTheme)
} else {
applyTheme(mediaPrefersDarkTheme.matches ? 'dark' : 'light')
function setTheme(theme) {
syncStoredTheme(theme);
applyTheme(theme);
}

// Watch the device's preferred theme and update theme if user did not select a theme
mediaPrefersDarkTheme.addEventListener('change', () => {
const selectedTheme = localStorage.getItem('theme')
if (!selectedTheme) {
applyTheme(mediaPrefersDarkTheme.matches ? 'dark' : 'light')
}
})

// Add handlers for theme selection buttons.
document.querySelector('[data-light-theme-toggle]').addEventListener('click', () => {
selectTheme('light')
})
document.querySelector('[data-dark-theme-toggle]').addEventListener('click', () => {
selectTheme('dark')
})
function toggleTheme() {
setTheme(getAppliedTheme() === 'dark' ? 'light' : 'dark');
}

function reEnableTransitions() {
// re-enable transitions when triggered after first-render to avoid fouc
// setTimeout(fn, 0) needed to give CSS at lease 1 frame without transitions and thus avoid FOUC
setTimeout(() => {
// executed after css has loaded & theme swiching has occured
document.documentElement.classList.toggle('theme-init', false);
}, 0);
}

function initThemeEvents() {
// Watch the device's preferred theme and update theme if user did not select a theme
mediaPrefersDarkTheme.addEventListener('change', () => {
// abort if the user already selected a theme
if (!!getStoredTheme()) return;
// update applied theme accordingly
applyTheme(getPreferredTheme());
});
// Add handlers for theme selection button
document
.querySelector('.theme-picker')
?.addEventListener('click', toggleTheme);
// Re-enable transitons only when all content has loaded
window.addEventListener('DOMContentLoaded', reEnableTransitions);
}

function initTheme() {
// apply stored or preferred theme
applyTheme(getStoredTheme() ?? getPreferredTheme());
initThemeEvents();
}

initTheme();
"

/// Renders an HTML anhor tag
pub fn anchor(
to href: String,
attrs attributes: List(#(String, String)),
with content: List(Html),
) -> Html {
h("a", [#("href", href), ..attributes], content)
}

pub type Link {
Link(label: String, to: String)
}

/// Renders a styled text link
pub fn text_link(
for link: Link,
attributes attributes: List(#(String, String)),
) -> Html {
let link_attributes = [#("class", "link"), ..attributes]

anchor(link.to, link_attributes, [text(link.label)])
}

/// Renders the tour's navbar as html
pub fn navbar(titled title: String, links links: List(Link)) -> Html {
let links = list.map(links, fn(l) { text_link(l, []) })

let nav_right_items = list.flatten([links, [theme_picker()]])

h("nav", [#("class", "navbar")], [
anchor("/", [#("class", "logo")], [
h(
"img",
[
#("src", "https://gleam.run/images/lucy/lucy.svg"),
#("alt", "Lucy the star, Gleam's mascot"),
],
[],
),
text(title),
]),
h("div", [#("class", "nav-right")], nav_right_items),
])
}

/// Renders a horizontal separator
pub fn separator(class: String) -> Html {
h("hr", [#("class", class <> "-separator")], [])
}
76 changes: 76 additions & 0 deletions static/css/code/color-schemes/atom-one.css
@@ -0,0 +1,76 @@
/*

Atom One Dark & Light by Daniel Gamage
Original One Dark Syntax theme from https://github.com/atom/one-dark-syntax

Tweaked for compat with light / dark themes

*/

:root {
/*
Atom One Dark

base: #282c34
mono-1: #abb2bf
mono-2: #818896
mono-3: #5c6370
hue-1: #56b6c2
hue-2: #61aeee
hue-3: #c678dd
hue-4: #98c379
hue-5: #e06c75
hue-5-2: #be5046
hue-6: #d19a66
hue-6-2: #e6c07b
*/

--code-background-dark: #282c34; /* base */
--code-token-base-dark: #abb2bf; /* mono-1 */
--code-token-punctuation-dark: #818896; /* mono-2 */
--code-token-operator-dark: #c678dd; /* hue-3 */
--code-token-keyword-dark: #c678dd; /* hue-3 */
--code-token-string-dark: #98c379; /* hue-4 */
--code-token-comment-dark: #5c6370; /* mono-3 */
--code-token-attribute-dark: #818896; /* mono-2 */
--code-token-function-dark: #61aeee; /* hue-2 */
--code-token-function-name-dark: #61aeee; /* hue-2 */
--code-token-function-param-dark: #abb2bf; /* mono-1 */
--code-token-boolean-dark: #d19a66; /* hue-6 */
--code-token-number-dark: #d19a66; /* hue-6 */
--code-token-selector-dark: #e6c07b; /* hue-6-2 */
--code-token-type-dark: #56b6c2; /* hue-1 */

/*
Atom One Light

base: #fafafa
mono-1: #383a42
mono-2: #686b77
mono-3: #a0a1a7
hue-1: #0184bb
hue-2: #4078f2
hue-3: #a626a4
hue-4: #50a14f
hue-5: #e45649
hue-5-2: #c91243
hue-6: #986801
hue-6-2: #c18401
*/

--code-background-light: #fafafa; /* base */
--code-token-base-light: #383a42; /* mono-1 */
--code-token-punctuation-light: #686b77; /* mono-2 */
--code-token-operator-light: #a626a4; /* hue-3 */
--code-token-keyword-light: #a626a4; /* hue-3 */
--code-token-string-light: #50a14f; /* hue-4 */
--code-token-comment-light: #a0a1a7; /* mono-3 */
--code-token-attribute-light: #686b77; /* mono-2 */
--code-token-function-light: #4078f2; /* hue-2 */
--code-token-function-name-light: #4078f2; /* hue-2 */
--code-token-function-param-light: #383a42; /* mono-1 */
--code-token-boolean-light: #986801; /* hue-6 */
--code-token-number-light: #986801; /* hue-6 */
--code-token-selector-light: #c18401; /* hue-6-2 */
--code-token-type-light: #0184bb; /* hue-1 */
}