diff --git a/.gitignore b/.gitignore
index b3ae1c1..faf06e4 100755
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,4 @@
CLAUDE.md
LEARNINGS.md
PLAN.md
+/memory
diff --git a/vue/index.html b/vue/index.html
index d033333..3e3c0e3 100644
--- a/vue/index.html
+++ b/vue/index.html
@@ -7,6 +7,9 @@
RSS-Reader
+
+
+
diff --git a/vue/src/App.vue b/vue/src/App.vue
index 5cec656..81546e5 100644
--- a/vue/src/App.vue
+++ b/vue/src/App.vue
@@ -1,8 +1,12 @@
diff --git a/vue/src/assets/base.css b/vue/src/assets/base.css
index 90ce73d..f69a769 100644
--- a/vue/src/assets/base.css
+++ b/vue/src/assets/base.css
@@ -23,6 +23,11 @@
/* semantic color variables for this project */
:root {
+ --headline-font-family: Glook, 'Courier New';
+ --content-font-family: Merriweather, Georgia, 'Times New Roman', Times, serif;
+ --headline-font-size-scale: 1;
+ --content-font-size-scale: 1;
+
--color-background: var(--vt-c-white);
--color-background-soft: var(--vt-c-white-soft);
--color-background-mute: var(--vt-c-white-mute);
diff --git a/vue/src/assets/main.css b/vue/src/assets/main.css
index 47cf232..7e7a708 100644
--- a/vue/src/assets/main.css
+++ b/vue/src/assets/main.css
@@ -69,8 +69,8 @@ a,
.feed-title {
cursor: pointer;
- font-family: Glook, 'Courier New';
- font-size: clamp(1.4rem, 5vw, 2rem);
+ font-family: var(--headline-font-family);
+ font-size: calc(clamp(1.4rem, 5vw, 2rem) * var(--headline-font-size-scale));
font-weight: bold;
color: var(--color-accent-2);
border-bottom: 1px solid #ccc;
@@ -84,8 +84,8 @@ a,
}
.feed-content {
- font-family: Merriweather, Georgia, 'Times New Roman', Times, serif;
- font-size: clamp(1rem, 3.5vw, 1.25rem);
+ font-family: var(--content-font-family);
+ font-size: calc(clamp(1rem, 3.5vw, 1.25rem) * var(--content-font-size-scale));
padding: 0 1em 1em;
overflow-wrap: break-word;
}
@@ -101,7 +101,7 @@ a,
.feed-content h3 {
padding: 0.5em 0;
- font-size: clamp(1rem, 3vw, 1.3rem);
+ font-size: calc(clamp(1rem, 3vw, 1.3rem) * var(--headline-font-size-scale));
font-weight: bold;
}
diff --git a/vue/src/components/AdminSettings.vue b/vue/src/components/AdminSettings.vue
new file mode 100644
index 0000000..6612d2a
--- /dev/null
+++ b/vue/src/components/AdminSettings.vue
@@ -0,0 +1,158 @@
+
+
+
+
+
Typography
+
+
+ Headline Size
+
+
+
+
+
+
+ Article Text Size
+
+
+
+
+
+
+ Headline Font
+
+
+
+
+ Article Text Font
+
+
+
+
+
+
diff --git a/vue/src/components/RssFeeds.vue b/vue/src/components/RssFeeds.vue
index 6a6572c..78623cf 100644
--- a/vue/src/components/RssFeeds.vue
+++ b/vue/src/components/RssFeeds.vue
@@ -324,8 +324,8 @@ onMounted(async () => {
cursor: pointer;
margin: 0;
padding: 0 1rem;
- font-family: 'Courier New';
- font-size: clamp(1.4rem, 5vw, 2rem);
+ font-family: var(--headline-font-family);
+ font-size: calc(clamp(1.4rem, 5vw, 2rem) * var(--headline-font-size-scale));
font-weight: bold;
line-height: 1.15;
color: var(--color-accent-2);
@@ -352,8 +352,8 @@ onMounted(async () => {
.article-feature__content {
padding: 0 1rem;
- font-family: Georgia, 'Times New Roman', Times, serif;
- font-size: clamp(1rem, 3.5vw, 1.25rem);
+ font-family: var(--content-font-family);
+ font-size: calc(clamp(1rem, 3.5vw, 1.25rem) * var(--content-font-size-scale));
line-height: 1.75;
overflow-wrap: break-word;
}
@@ -364,7 +364,7 @@ onMounted(async () => {
.article-feature__content :deep(h3) {
padding: 0.5em 0;
- font-size: clamp(1rem, 3vw, 1.3rem);
+ font-size: calc(clamp(1rem, 3vw, 1.3rem) * var(--headline-font-size-scale));
font-weight: bold;
}
@@ -421,7 +421,7 @@ onMounted(async () => {
padding: 1em 0;
border-top: 1px solid var(--color-border);
border-bottom: 1px solid var(--color-border);
- font-family: Georgia, 'Times New Roman', Times, serif;
+ font-family: var(--content-font-family);
font-size: 1.25em;
font-style: italic;
text-align: center;
diff --git a/vue/src/composables/useSettings.js b/vue/src/composables/useSettings.js
new file mode 100644
index 0000000..1dccc55
--- /dev/null
+++ b/vue/src/composables/useSettings.js
@@ -0,0 +1,79 @@
+import { ref } from 'vue'
+
+const HEADLINE_FONT_OPTIONS = [
+ { key: 'default', label: 'Default (Glook)', value: "Glook, 'Courier New'" },
+ { key: 'playfair', label: 'Playfair Display', value: "'Playfair Display', Georgia, serif" },
+ { key: 'lora', label: 'Lora', value: "Lora, Georgia, serif" },
+ { key: 'raleway', label: 'Raleway', value: "Raleway, -apple-system, sans-serif" },
+ { key: 'inter', label: 'Inter', value: "Inter, -apple-system, sans-serif" },
+]
+
+const CONTENT_FONT_OPTIONS = [
+ { key: 'default', label: 'Default (Merriweather)', value: "Merriweather, Georgia, 'Times New Roman', Times, serif" },
+ { key: 'lora', label: 'Lora', value: "Lora, Georgia, serif" },
+ { key: 'source-serif', label: 'Source Serif 4', value: "'Source Serif 4', Georgia, serif" },
+ { key: 'inter', label: 'Inter', value: "Inter, -apple-system, sans-serif" },
+ { key: 'playfair', label: 'Playfair Display', value: "'Playfair Display', Georgia, serif" },
+]
+
+const SIZE_STEPS = [0.85, 1, 1.2, 1.45]
+const SIZE_LABELS = ['S', 'M', 'L', 'XL']
+
+const headlineSizeScale = ref(parseFloat(localStorage.getItem('s-headline-size') ?? '1'))
+const contentSizeScale = ref(parseFloat(localStorage.getItem('s-content-size') ?? '1'))
+const headlineFontKey = ref(localStorage.getItem('s-headline-font') ?? 'default')
+const contentFontKey = ref(localStorage.getItem('s-content-font') ?? 'default')
+
+function fontValue(options, key) {
+ return (options.find(o => o.key === key) ?? options[0]).value
+}
+
+function applySettings() {
+ const s = document.documentElement.style
+ s.setProperty('--headline-font-size-scale', headlineSizeScale.value)
+ s.setProperty('--content-font-size-scale', contentSizeScale.value)
+ s.setProperty('--headline-font-family', fontValue(HEADLINE_FONT_OPTIONS, headlineFontKey.value))
+ s.setProperty('--content-font-family', fontValue(CONTENT_FONT_OPTIONS, contentFontKey.value))
+}
+
+function setHeadlineSize(scale) {
+ headlineSizeScale.value = scale
+ localStorage.setItem('s-headline-size', scale)
+ applySettings()
+}
+
+function setContentSize(scale) {
+ contentSizeScale.value = scale
+ localStorage.setItem('s-content-size', scale)
+ applySettings()
+}
+
+function setHeadlineFont(key) {
+ headlineFontKey.value = key
+ localStorage.setItem('s-headline-font', key)
+ applySettings()
+}
+
+function setContentFont(key) {
+ contentFontKey.value = key
+ localStorage.setItem('s-content-font', key)
+ applySettings()
+}
+
+export function useSettings() {
+ return {
+ headlineSizeScale,
+ contentSizeScale,
+ headlineFontKey,
+ contentFontKey,
+ SIZE_STEPS,
+ SIZE_LABELS,
+ HEADLINE_FONT_OPTIONS,
+ CONTENT_FONT_OPTIONS,
+ applySettings,
+ setHeadlineSize,
+ setContentSize,
+ setHeadlineFont,
+ setContentFont,
+ }
+}
diff --git a/vue/src/router/index.js b/vue/src/router/index.js
index c2b311c..8e0aefd 100644
--- a/vue/src/router/index.js
+++ b/vue/src/router/index.js
@@ -2,6 +2,7 @@ import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
+ scrollBehavior: () => ({ top: 0, behavior: 'instant' }),
routes: [
{
path: '/',
diff --git a/vue/src/views/AdminView.vue b/vue/src/views/AdminView.vue
index 60c7a38..79f8ebd 100644
--- a/vue/src/views/AdminView.vue
+++ b/vue/src/views/AdminView.vue
@@ -1,9 +1,11 @@
+