Skip to content

Commit fe045ac

Browse files
feat: add configuration system for section and element visibility
- Add config.yml for toggling visibility of sections and UI elements - Implement config utility for dynamic visibility handling - Add visibility controls for work experience, talks, writing, and social links - Add visibility controls for avatar, theme switch, footer, and header - Wrap components with config checks throughout the app - Install js-yaml for YAML parsing - Supports live updates without server restart (auto-refresh on save) Resolves #15
1 parent b4e6b14 commit fe045ac

File tree

8 files changed

+139
-55
lines changed

8 files changed

+139
-55
lines changed

bun.lockb

403 Bytes
Binary file not shown.

config.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Site Configuration - Control visibility of sections
2+
sections:
3+
about: true
4+
workExperience: true
5+
talks: true
6+
writing: true
7+
socialLinks: true
8+
9+
# Individual elements
10+
elements:
11+
avatar: true
12+
themeSwitch: true
13+
header: true
14+
footer: true

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@
1414
"@astrojs/sitemap": "3.6.0",
1515
"@tailwindcss/typography": "^0.5.19",
1616
"@tailwindcss/vite": "^4.1.16",
17+
"@types/js-yaml": "^4.0.9",
1718
"@types/react": "^18.3.3",
1819
"@types/react-dom": "^18.3.0",
1920
"astro": "5.15.2",
2021
"framer-motion": "^12.23.24",
22+
"js-yaml": "^4.1.0",
2123
"lucide-react": "^0.548.0",
2224
"mdast-util-to-string": "^4.0.0",
2325
"react": "^18.3.1",
Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
---
22
import Container from '@/components/Container.astro';
3+
import { getSiteConfig } from '@/lib/config';
4+
5+
const config = getSiteConfig();
36
---
47

5-
<Container as='footer' class='pt-24'>
6-
<p class="text-center text-muted-foreground text-sm">
7-
&copy; {new Date().getFullYear()}. Powered by <a href="https://astro.build" target="_blank" rel="noopener noreferrer">Astro</a> and CVfolio.
8-
</p>
9-
</Container>
8+
{config.elements.footer && (
9+
<Container as='footer' class='pt-24'>
10+
<p class="text-center text-muted-foreground text-sm">
11+
&copy; {new Date().getFullYear()}. Powered by <a href="https://astro.build" target="_blank" rel="noopener noreferrer">Astro</a> and CVfolio.
12+
</p>
13+
</Container>
14+
)}
Lines changed: 40 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,49 @@
11
---
22
import Container from '@/components/Container.astro';
3+
import { getSiteConfig } from '@/lib/config';
34
45
const pathname = Astro.url.pathname;
56
67
const isHomePage = pathname === '/';
78
const isWritingPage = pathname.startsWith('/writing');
8-
---
99
10-
<Container
11-
as="header"
12-
class="w-full max-w-full flex justify-center items-center"
13-
>
14-
<div
15-
class="w-max fixed top-0 mt-5 bg-muted-foreground/40 backdrop-blur-3xl border border-border rounded-full p-1"
10+
const config = getSiteConfig();
11+
---
12+
{config.elements.header && (
13+
<Container
14+
as="header"
15+
class="w-full max-w-full flex justify-center items-center"
1616
>
17-
<nav class="flex items-center">
18-
<ul class="flex items-center gap-1">
19-
<li>
20-
<a
21-
href="/"
22-
class:list={[
23-
'font-medium transition-colors block px-5 py-2',
24-
'hover:text-headings',
25-
isHomePage && 'text-headings bg-muted-foreground/40 rounded-full',
26-
]}>Home</a
27-
>
28-
</li>
29-
<li>
30-
<a
31-
href="/writing"
32-
class:list={[
33-
'font-medium transition-colors block px-5 py-2',
34-
'hover:text-headings',
35-
isWritingPage &&
36-
'text-headings bg-muted-foreground/40 rounded-full',
37-
]}>Writing</a
38-
>
39-
</li>
40-
</ul>
41-
</nav>
42-
</div>
43-
</Container>
17+
<div
18+
class="w-max fixed top-0 mt-5 bg-muted-foreground/40 backdrop-blur-3xl border border-border rounded-full p-1"
19+
>
20+
<nav class="flex items-center">
21+
<ul class="flex items-center gap-1">
22+
<li>
23+
<a
24+
href="/"
25+
class:list={[
26+
'font-medium transition-colors block px-5 py-2',
27+
'hover:text-headings',
28+
isHomePage && 'text-headings bg-muted-foreground/40 rounded-full',
29+
]}>Home</a
30+
>
31+
</li>
32+
{config.sections.writing && (
33+
<li>
34+
<a
35+
href="/writing"
36+
class:list={[
37+
'font-medium transition-colors block px-5 py-2',
38+
'hover:text-headings',
39+
isWritingPage &&
40+
'text-headings bg-muted-foreground/40 rounded-full',
41+
]}>Writing</a
42+
>
43+
</li>
44+
)}
45+
</ul>
46+
</nav>
47+
</div>
48+
</Container>
49+
)}

src/layouts/BaseLayout.astro

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@ import Header from '@/components/partials/Header.astro';
66
import Footer from '@/components/partials/Footer.astro';
77
import Head from '@/components/partials/Head.astro';
88
import SwitchTheme from '@/components/SwitchTheme.tsx';
9+
import { getSiteConfig } from '@/lib/config';
910
1011
interface Props {
1112
seo?: Seo
1213
}
1314
1415
const { seo } = Astro.props;
16+
const config = getSiteConfig();
1517
---
1618

1719
<html lang="en">
@@ -22,6 +24,8 @@ const { seo } = Astro.props;
2224
<slot />
2325
</main>
2426
<Footer />
25-
<SwitchTheme client:only="react" />
27+
{config.elements.themeSwitch && (
28+
<SwitchTheme client:only="react" />
29+
)}
2630
</body>
2731
</html>

src/lib/config.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import fs from 'fs';
2+
import path from 'path';
3+
import yaml from 'js-yaml';
4+
5+
interface SiteConfig {
6+
sections: {
7+
about: boolean;
8+
workExperience: boolean;
9+
talks: boolean;
10+
writing: boolean;
11+
socialLinks: boolean;
12+
};
13+
elements: {
14+
avatar: boolean;
15+
themeSwitch: boolean;
16+
header: boolean;
17+
footer: boolean;
18+
};
19+
}
20+
21+
const defaultConfig: SiteConfig = {
22+
sections: {
23+
about: true,
24+
workExperience: true,
25+
talks: true,
26+
writing: true,
27+
socialLinks: true,
28+
},
29+
elements: {
30+
avatar: true,
31+
themeSwitch: true,
32+
footer: true,
33+
header: true,
34+
},
35+
};
36+
37+
export function getSiteConfig(): SiteConfig {
38+
try {
39+
const configPath = path.join(process.cwd(), 'config.yml');
40+
const fileContents = fs.readFileSync(configPath, 'utf8');
41+
return yaml.load(fileContents) as SiteConfig;
42+
} catch (error) {
43+
console.warn('config.yml not found, using defaults');
44+
return defaultConfig;
45+
}
46+
}

src/pages/index.astro

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { DEFAULT_CONFIGURATION } from '@/lib/constants';
77
import WorkExperience from '@/components/ui/WorkExperience.astro';
88
import Talk from '@/components/ui/Talk.astro';
99
import { sortByDateRange, sortByYear } from '@/lib/utils';
10+
import { getSiteConfig } from '@/lib/config';
1011
1112
const entry = await getEntry('pages', 'homepage');
1213
@@ -23,25 +24,31 @@ const sortedJobs = sortByDateRange(jobs);
2324
2425
const talks = await getCollection('talks');
2526
const sortedTalks = sortByYear(talks);
27+
28+
const config = getSiteConfig();
2629
---
2730

2831
<BaseLayout seo={entry.data.seo}>
29-
<Container as="section" class="py-6">
30-
<Author {...DEFAULT_CONFIGURATION.author} />
31-
</Container>
32+
{config.elements.avatar && (
33+
<Container as="section" class="py-6">
34+
<Author {...DEFAULT_CONFIGURATION.author} />
35+
</Container>
36+
)}
3237

33-
<Container as="section" class="py-6">
34-
<div class="flex flex-col gap-6">
35-
<div class="flex items-center">
36-
<span class="text-headings">About</span>
37-
</div>
38-
<div class="prose dark:prose-invert">
39-
<Content />
38+
{config.sections.about && (
39+
<Container as="section" class="py-6">
40+
<div class="flex flex-col gap-6">
41+
<div class="flex items-center">
42+
<span class="text-headings">About</span>
43+
</div>
44+
<div class="prose dark:prose-invert">
45+
<Content />
46+
</div>
4047
</div>
41-
</div>
42-
</Container>
48+
</Container>
49+
)}
4350
{
44-
links.length > 0 && (
51+
config.sections.socialLinks && links.length > 0 && (
4552
<Container as="section" class="py-8">
4653
<div class="flex flex-col gap-5">
4754
<span class="text-headings">Contact</span>
@@ -69,7 +76,7 @@ const sortedTalks = sortByYear(talks);
6976
)
7077
}
7178
{
72-
sortedJobs.length > 0 && (
79+
config.sections.workExperience && sortedJobs.length > 0 && (
7380
<Container as="section" class="py-6">
7481
<div class="flex flex-col gap-5">
7582
<span class="text-headings">Work Experience</span>
@@ -83,7 +90,7 @@ const sortedTalks = sortByYear(talks);
8390
)
8491
}
8592
{
86-
talks.length > 0 && (
93+
config.sections.talks && sortedTalks.length > 0 && (
8794
<Container as="section" class="py-6">
8895
<div class="flex flex-col gap-5">
8996
<span class="text-headings">Speaking</span>

0 commit comments

Comments
 (0)