Browse Source

theme switching, default themes, storing settings

theming
Will Webberley 1 month ago
parent
commit
dbb433f0f2
8 changed files with 131 additions and 49 deletions
  1. 3
      package.json
  2. 2
      src/components/BlogPostHeader.js
  3. 63
      src/components/Header.js
  4. 21
      src/components/Layout/Layout.css
  5. 78
      src/components/Layout/Layout.js
  6. 2
      src/pages/index.js
  7. 6
      src/store.js
  8. 5
      yarn.lock

3
package.json

@ -17,7 +17,8 @@
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-helmet": "^5.2.1",
"styled-components": "^5.2.1"
"styled-components": "^5.2.1",
"zustand": "^3.3.1"
},
"scripts": {
"build": "gatsby build",

2
src/components/BlogPostHeader.js

@ -9,7 +9,7 @@ const BlogPostHeader = ({ post }) => (
<h3><Emoji e='📝' /> <Link to={post.fields.slug}>{post.frontmatter.title}</Link> <small>{moment(post.frontmatter.date).format('D MMMM YYYY')}</small></h3>
<p><em>{post.excerpt}</em></p>
{post.frontmatter.tags && post.frontmatter.tags.map(tag =>
<Link style={{marginRight: 10}} to={`/tags/${tag}`}><Emoji e="🏷️" /> #{tag}</Link>
<Link key={tag} style={{marginRight: 10}} to={`/tags/${tag}`}><Emoji e="🏷️" /> #{tag}</Link>
)}
</div>
)

63
src/components/Header.js

@ -2,6 +2,8 @@ import React from 'react'
import { Link } from 'gatsby'
import styled from 'styled-components';
import Emoji from './Emoji';
import useStore from '../store';
import { themes } from '../components/Layout/Layout'
import Will from '../images/will.jpg';
@ -53,26 +55,47 @@ const StyledNavBar = styled.div`
font-size: 20px;
}
`;
const StyledThemeSelector = styled.select`
padding: 4px;
background: white;
border: 1px solid gray;
border-radius: 5px;
cursor: pointer;
`;
const Header = () => {
const store = useStore();
const switchTheme = themeName => {
store.setTheme(themeName);
localStorage.setItem('theme', themeName);
};
const Header = () => (
<StyledHeader>
<StyledHeaderArea>
<Link to='/'><StyledAvatar /></Link>
<div>
<StyledTitle><Link to='/'>Will Webberley</Link></StyledTitle>
<StyledSocialLink rel="me" href="https://fosstodon.org/@wilw"><Emoji e='📣' /> mastodon</StyledSocialLink>
<StyledSocialLink href="https://pixelfed.social/@wilw" target="_blank" rel="noopener noreferrer"><Emoji e='🖼️' /> pixelfed</StyledSocialLink>
<StyledSocialLink href="https://git.wilw.dev/explore/repos" target="_blank" rel="noopener noreferrer"><Emoji e='💻' /> code</StyledSocialLink>
</div>
</StyledHeaderArea>
<StyledNavBar>
<Link to='/' activeClassName='active-link'>about</Link>
<Link to='/blog' activeClassName='active-link'>blog</Link>
<Link to='/notes' activeClassName='active-link'>notes</Link>
<Link to='/projects' activeClassName='active-link'>projects</Link>
<Link to='/research' activeClassName='active-link'>research</Link>
</StyledNavBar>
</StyledHeader>
)
return (
<StyledHeader>
<StyledHeaderArea>
<Link to='/'><StyledAvatar /></Link>
<div>
<StyledTitle><Link to='/'>Will Webberley</Link></StyledTitle>
<StyledSocialLink rel="me" href="https://fosstodon.org/@wilw"><Emoji e='📣' /> mastodon</StyledSocialLink>
<StyledSocialLink href="https://pixelfed.social/@wilw" target="_blank" rel="noopener noreferrer"><Emoji e='🖼️' /> pixelfed</StyledSocialLink>
<StyledSocialLink href="https://git.wilw.dev/explore/repos" target="_blank" rel="noopener noreferrer"><Emoji e='💻' /> code</StyledSocialLink>
</div>
</StyledHeaderArea>
<StyledNavBar>
<Link to='/' activeClassName='active-link'>about</Link>
<Link to='/blog' activeClassName='active-link'>blog</Link>
<Link to='/notes' activeClassName='active-link'>notes</Link>
<Link to='/projects' activeClassName='active-link'>projects</Link>
<Link to='/research' activeClassName='active-link'>research</Link>
<StyledThemeSelector value={store.theme} onChange={e => switchTheme(e.target.value)}>
{Object.keys(themes).map(themeName =>
<option key={themeName} value={themeName}>{themes[themeName].name}</option>
)}
</StyledThemeSelector>
</StyledNavBar>
</StyledHeader>
);
}
export default Header

21
src/components/Layout/Layout.css

@ -12,19 +12,6 @@ html {
overflow-y: scroll;
}
a {
color: #01BAEF;
background-color: transparent;
-webkit-text-decoration-skip: objects;
}
a:active,
a:hover {
outline-width: 0;
}
a.active-link{
color:rgb(50,50,50);
}
body {
margin: 0;
font-family: 'Recursive', monospace;
@ -84,6 +71,14 @@ h4 {
line-height: 1.3;
}
a {
background-color: transparent;
-webkit-text-decoration-skip: objects;
}
a:active, a:hover {
outline-width: 0;
}
p {
margin: 0px 0px 15px 0px;
}

78
src/components/Layout/Layout.js

@ -1,11 +1,47 @@
import React from 'react'
import React, { useCallback, useEffect } from 'react'
import Helmet from 'react-helmet'
import styled from 'styled-components'
import styled, { createGlobalStyle } from 'styled-components'
import Header from '../Header'
import Footer from '../Footer';
import useStore from '../../store';
import Will from '../../images/will.png';
import './Layout.css'
import './Layout.css';
export const themes = {
light: {
name: '☀️ Light Theme',
background: 'white',
text: 'black',
links: '#01BAEF',
activeLinks: 'rgb(50,50,50)',
},
dark: {
name: '🌒 Dark Theme',
background: '#0f0e17',
text: '#a7a9be',
headers: '#fffffe',
links: '#ff8906',
activeLinks: 'rgb(200,200,200)',
}
}
const GlobalStyle = createGlobalStyle`
body{
background-color: ${({ theme }) => theme.background};
color: ${({ theme }) => theme.text};
transition: background-color 0.25s, color 0.25s;
}
h1,h2,h3,h4 {
color: ${({ theme }) => theme.headers};
}
a {
color: ${({ theme }) => theme.links};
&.active-link {
color: ${({ theme }) => theme.activeLinks};
}
}
`;
const StyledMain = styled.div`
margin: 0px auto;
@ -13,15 +49,31 @@ const StyledMain = styled.div`
padding: 0px 1.1rem 1.45rem;
`;
const TemplateWrapper = ({ children }) => (
<div>
<Helmet titleTemplate="%s | Will Webberley" defaultTitle="Will Webberley">
<link rel="icon" type="image/png" href={Will} />
</Helmet>
<Header />
<StyledMain>{children}</StyledMain>
<Footer />
</div>
)
const TemplateWrapper = ({ children }) => {
const theme = useStore(s => s.theme);
const setTheme = useStore(s => s.setTheme);
useEffect(() => {
const rememberedTheme = localStorage.getItem('theme');
if (rememberedTheme && themes[rememberedTheme]) {
setTheme(rememberedTheme);
} else {
const isDarkMode = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
if (isDarkMode) setTheme('dark');
}
}, [setTheme]);
return (
<div>
<GlobalStyle theme={themes[theme]} />
<Helmet titleTemplate="%s | Will Webberley" defaultTitle="Will Webberley">
<link rel="icon" type="image/png" href={Will} />
</Helmet>
<Header />
<StyledMain>{children}</StyledMain>
<Footer />
</div>
);
}
export default TemplateWrapper

2
src/pages/index.js

@ -46,7 +46,7 @@ const IndexPage = (props) => (
<h3>Recent posts</h3>
{props.data.recentPosts.edges
.map(({ node }) => node.fields.slug.indexOf('blog') > -1 ? <ArticleHeader post={node} /> : null)}
.map(({ node }) => node.fields.slug.indexOf('blog') > -1 ? <ArticleHeader key={node.fields.slug} post={node} /> : null)}
<p><Link to='/blog'>See more</Link></p>
</Layout>
)

6
src/store.js

@ -0,0 +1,6 @@
import create from 'zustand'
export default create(set => ({
theme: 'light',
setTheme: theme => set({ theme }),
}));

5
yarn.lock

@ -12378,6 +12378,11 @@ yurnalist@^2.1.0:
read "^1.0.7"
strip-ansi "^5.2.0"
zustand@^3.3.1:
version "3.3.1"
resolved "https://registry.yarnpkg.com/zustand/-/zustand-3.3.1.tgz#de5c4b51112b84e0f819d8b3f336fbfbc087d758"
integrity sha512-o0rgrBsi29nCkPHdhtkAHisCIlmRUoXOV+1AmDMeCgkGG0i5edFSpGU0KiZYBvFmBYycnck4Z07JsLYDjSET9g==
zwitch@^1.0.0:
version "1.0.5"
resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-1.0.5.tgz#d11d7381ffed16b742f6af7b3f223d5cd9fe9920"

Loading…
Cancel
Save