move theme related data from blog to theme

This commit is contained in:
Kim, Jimin 2022-12-15 20:04:18 +09:00
parent 2965ca04b0
commit 7b7be55499
68 changed files with 1393 additions and 1251 deletions

View file

@ -24,7 +24,6 @@
"rules": {
"@typescript-eslint/no-empty-interface": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
"react/jsx-uses-vars": "error",
"react/react-in-jsx-scope": ["off"]
}
}

View file

@ -9,6 +9,7 @@
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf build"
},
"dependencies": {
"@developomp-site/theme": "workspace:*",
"@fortawesome/fontawesome-svg-core": "^6.2.1",
"@fortawesome/free-brands-svg-icons": "^6.2.1",
"@fortawesome/free-regular-svg-icons": "^6.2.1",
@ -33,8 +34,8 @@
"styled-components": "^5.3.6"
},
"devDependencies": {
"@developomp-site/tsconfig": "workspace:0.0.0",
"@developomp-site/eslint-config": "workspace:0.0.0",
"@developomp-site/eslint-config": "workspace:*",
"@developomp-site/tsconfig": "workspace:*",
"@types/ejs": "^3.1.1",
"@types/elasticlunr": "^0.9.5",
"@types/highlight.js": "^10.1.0",

View file

@ -1,3 +1,6 @@
import darkTheme from "@developomp-site/theme/dist/dark.json"
import lightTheme from "@developomp-site/theme/dist/light.json"
import { useContext, useEffect, useState } from "react"
import { Routes, Route, useNavigate, useLocation } from "react-router-dom"
import styled, { ThemeProvider } from "styled-components"
@ -5,7 +8,7 @@ import { Helmet } from "react-helmet-async"
import { isIE } from "react-device-detect"
import Loading from "./components/Loading"
import Navbar from "./components/Navbar"
import Header from "./components/Header"
import Footer from "./components/Footer"
import Home from "./pages/Home"
@ -14,17 +17,16 @@ import Page from "./pages/Page"
import NotFound from "./pages/NotFound"
import Portfolio from "./pages/Portfolio"
import theming from "./styles/theming"
import GlobalStyle from "./styles/globalStyle"
import { ActionsEnum, globalContext } from "./globalContext"
import { globalContext } from "./globalContext"
const IENotSupported = styled.p`
margin: auto;
font-size: 2rem;
margin-top: 2rem;
text-align: center;
font-family: ${theming.font.regular};
font-family: ${(props) => props.theme.theme.font.sansSerif};
`
const StyledContentContainer = styled.div`
@ -34,7 +36,7 @@ const StyledContentContainer = styled.div`
`
export default function App() {
const { globalState, dispatch } = useContext(globalContext)
const { globalState } = useContext(globalContext)
const { locale } = globalState
const navigate = useNavigate()
@ -73,10 +75,8 @@ export default function App() {
return (
<ThemeProvider
theme={{
currentTheme: globalState.theme,
setTheme(setThemeTo) {
dispatch({ type: ActionsEnum.UPDATE_THEME, payload: setThemeTo })
},
currentTheme: globalState.currentTheme,
theme: globalState.currentTheme === "dark" ? darkTheme : lightTheme,
}}
>
<Helmet>
@ -87,7 +87,7 @@ export default function App() {
<GlobalStyle />
<Navbar />
<Header />
<StyledContentContainer>
{isLoading ? (
<Loading />

View file

@ -1,8 +1,9 @@
import dark from "@developomp-site/theme/dist/dark.json"
import light from "@developomp-site/theme/dist/light.json"
import { useEffect, useState } from "react"
import styled from "styled-components"
import theming from "../styles/theming"
const StyledBadge = styled.div<{ color: string; isDark: boolean }>`
vertical-align: middle;
display: inline-block;
@ -15,7 +16,7 @@ const StyledBadge = styled.div<{ color: string; isDark: boolean }>`
background-color: ${(props) => props.color};
color: ${(props) =>
props.isDark ? theming.dark.color1 : theming.light.color1};
props.isDark ? dark.color.text.default : light.color.text.default};
`
const StyledSVG = styled.div<{ isDark: boolean }>`
@ -27,7 +28,9 @@ const StyledSVG = styled.div<{ isDark: boolean }>`
svg {
height: 16px;
fill: ${(props) =>
props.isDark ? theming.dark.color1 : theming.light.color1} !important;
props.isDark
? dark.color.text.default
: light.color.text.default} !important;
}
`

View file

@ -1,29 +1,22 @@
import styled, { css } from "styled-components"
import theming from "../styles/theming"
export const cardCSS = css`
margin: auto;
background-color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: "white",
dark: theming.dark.backgroundColor2,
})};
background-color: ${({ theme }) =>
theme.currentTheme ? theme.theme.component.card.color.background : "white"};
padding: 2rem;
border-radius: 6px;
box-shadow: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: "0 4px 10px rgb(0 0 0 / 5%), 0 0 1px rgb(0 0 0 / 10%);",
dark: "0 4px 10px rgb(0 0 0 / 30%), 0 0 1px rgb(0 0 0 / 30%);",
})};
box-shadow: ${({ theme }) =>
theme.currentTheme === "dark"
? "0 4px 10px rgb(0 0 0 / 30%), 0 0 1px rgb(0 0 0 / 30%)"
: "0 4px 10px rgb(0 0 0 / 5%), 0 0 1px rgb(0 0 0 / 10%)"};
@media screen and (max-width: ${theming.size.screen_size1}) {
@media screen and (max-width: ${({ theme }) =>
theme.theme.maxDisplayWidth.mobile}) {
padding: 1rem;
}
`
const Card = styled.div`
export default styled.div`
${cardCSS}
`
export default Card

View file

@ -1,6 +1,5 @@
import styled from "styled-components"
import theming from "../../styles/theming"
import GithubLinkIcon from "../GithubLinkIcon"
const StyledFooter = styled.footer`
@ -13,17 +12,8 @@ const StyledFooter = styled.footer`
align-items: center;
justify-content: center;
color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: "black",
dark: "white",
})};
background-color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: "white",
dark: "black",
})};
background-color: ${({ theme }) =>
theme.theme.component.footer.color.background};
`
const StyledFooterContainer = styled.div`
@ -35,10 +25,10 @@ const StyledFooterContainer = styled.div`
color: gray;
width: 100%;
max-width: ${theming.size.screen_size2};
max-width: ${({ theme }) => theme.theme.maxDisplayWidth.desktop};
`
const Footer = () => {
export default () => {
return (
<StyledFooter>
<StyledFooterContainer>
@ -51,5 +41,3 @@ const Footer = () => {
</StyledFooter>
)
}
export default Footer

View file

@ -4,39 +4,27 @@ import styled from "styled-components"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import { faGithub } from "@fortawesome/free-brands-svg-icons"
import theming from "../styles/theming"
const StyledGithubLink = styled.a<{ size?: string }>`
font-size: ${(props) => props.size || "2.5rem"};
color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: "lightgrey",
dark: "grey",
})};
color: ${({ theme }) =>
theme.currentTheme === "dark" ? "grey" : "lightgrey"};
:hover {
color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: theming.light.color0,
dark: theming.dark.color0,
})};
color: ${({ theme }) => theme.theme.color.text.highContrast};
}
`
interface GithubLinkIconProps {
interface Props {
link: string
size?: string
children?: ReactNode
}
const GithubLinkIcon = (props: GithubLinkIconProps) => {
export default ({ link, size, children }: Props) => {
return (
<StyledGithubLink size={props.size} href={props.link} target="_blank">
<StyledGithubLink size={size} href={link} target="_blank">
<FontAwesomeIcon icon={faGithub} />
{props.children}
{children}
</StyledGithubLink>
)
}
export default GithubLinkIcon

View file

@ -0,0 +1,21 @@
import styled from "styled-components"
import LocaleToggleButton from "./LocaleToggleButton"
import ThemeToggleButton from "./ThemeToggleButton"
import SearchButton from "./SearchButton"
const RightButtons = styled.div`
display: flex;
height: 100%;
margin-left: auto;
`
export default () => {
return (
<RightButtons>
<LocaleToggleButton />
<ThemeToggleButton />
<SearchButton />
</RightButtons>
)
}

View file

@ -1,3 +1,5 @@
import type { SiteLocale } from "../../../globalContext"
import { useContext } from "react"
import styled from "styled-components"
import ReactTooltip from "react-tooltip"
@ -5,29 +7,28 @@ import ReactTooltip from "react-tooltip"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import { faLanguage } from "@fortawesome/free-solid-svg-icons"
import { ActionsEnum, globalContext } from "../../globalContext"
import theming from "../../styles/theming"
import { ActionsEnum, globalContext } from "../../../globalContext"
import { HeaderButtonCSS } from "../HeaderButton"
import type { SiteLocale } from "../../globalContext"
interface StyledLocaleToggleButtonProps {
interface Props {
locale: SiteLocale
}
const StyledLocaleToggleButton = styled.button<StyledLocaleToggleButtonProps>`
${theming.styles.navbarButtonStyle}
const LocaleToggleButton = styled.button<Props>`
${HeaderButtonCSS}
border: none;
width: 72px;
${(props) => (props.locale == "en" ? "" : "transform: scaleX(-1);")};
`
function LocaleToggleButton() {
export default () => {
const { globalState, dispatch } = useContext(globalContext)
return (
<>
<StyledLocaleToggleButton
<LocaleToggleButton
data-tip
data-for="locale"
onClick={() => {
@ -39,12 +40,10 @@ function LocaleToggleButton() {
locale={globalState.locale}
>
<FontAwesomeIcon icon={faLanguage} />
</StyledLocaleToggleButton>
</LocaleToggleButton>
<ReactTooltip id="locale" type="dark" effect="solid">
<span>{globalState.locale == "en" ? "English" : "한국어"} </span>
</ReactTooltip>
</>
)
}
export default LocaleToggleButton

View file

@ -2,12 +2,11 @@ import { useContext } from "react"
import { Link } from "react-router-dom"
import ReactTooltip from "react-tooltip"
import HeaderButton from "../HeaderButton"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import { faSearch } from "@fortawesome/free-solid-svg-icons"
import { StyledLink } from "./Navbar"
import { globalContext } from "../../globalContext"
import { globalContext } from "../../../globalContext"
const SearchButton = () => {
const { globalState } = useContext(globalContext)
@ -17,9 +16,9 @@ const SearchButton = () => {
<>
<div>
<Link data-tip data-for="search" to={`/${locale}/search`}>
<StyledLink>
<HeaderButton>
<FontAwesomeIcon icon={faSearch} />
</StyledLink>
</HeaderButton>
</Link>
</div>
<ReactTooltip id="search" type="dark" effect="solid">

View file

@ -6,24 +6,21 @@ import ReactTooltip from "react-tooltip"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import { faMoon, faSun } from "@fortawesome/free-solid-svg-icons"
import theming from "../../styles/theming"
import { ActionsEnum, globalContext } from "../../globalContext"
import { ActionsEnum, globalContext } from "../../../globalContext"
import { HeaderButtonCSS } from "../HeaderButton"
const StyledThemeButton = styled.button`
${theming.styles.navbarButtonStyle}
${HeaderButtonCSS}
border: none;
width: 72px;
${(props) =>
theming.theme(props.theme.currentTheme, {
light: "",
dark: "transform: scaleX(-1);",
})};
${({ theme }) =>
theme.currentTheme === "dark" ? "transform: scaleX(-1)" : ""};
`
const ThemeToggleButton = () => {
const { globalState, dispatch } = useContext(globalContext)
const theme = globalState.theme
const theme = globalState.currentTheme
return (
<>

View file

@ -0,0 +1,3 @@
import Buttons from "./Buttons"
export default Buttons

View file

@ -0,0 +1,63 @@
import { useContext } from "react"
import { Link } from "react-router-dom"
import styled from "styled-components"
import ReadProgress from "./ReadProgress"
import Nav from "./Nav"
import Sidebar from "../Sidebar"
import { globalContext } from "../../globalContext"
import Buttons from "./Buttons"
const Header = styled.header`
/* set z index to arbitrarily high value to prevent other components from drawing over it */
z-index: 9999;
position: fixed;
width: 100%;
background-color: ${({ theme }) =>
theme.theme.component.ui.color.background.default};
color: ${({ theme }) => theme.theme.color.text.default};
box-shadow: 0 4px 10px rgb(0 0 0 / 5%);
`
const Container = styled.div`
margin: 0 auto;
align-items: center;
display: flex;
height: 4rem;
/* account for 20px scrollbar width */
@media only screen and (min-width: calc(${({ theme }) =>
theme.theme.maxDisplayWidth.desktop} + 20px)) {
width: calc(${({ theme }) => theme.theme.maxDisplayWidth.desktop} - 20px);
}
`
const Icon = styled.img`
height: 2.5rem;
display: block;
margin: 1rem;
`
export default () => {
const { globalState } = useContext(globalContext)
const { locale } = globalState
return (
<Header>
<Container>
<Link to={`/${locale}`}>
<Icon src="/icon/icon_circle.svg" />
</Link>
<Nav />
<Buttons />
<Sidebar />
</Container>
<ReadProgress />
</Header>
)
}

View file

@ -0,0 +1,43 @@
/**
* @file Manages the configuration settings for the widget.
*/
import styled, { css } from "styled-components"
export const HeaderButtonCSS = css`
/* style */
display: flex;
cursor: pointer;
align-items: center;
justify-content: center;
/* size */
height: 100%;
min-width: 2.5rem;
margin: 0;
padding: 0 1rem 0 1rem;
/* text */
text-decoration: none;
/* color */
color: ${({ theme }) => theme.theme.color.text.default};
background-color: ${({ theme }) =>
theme.theme.component.ui.color.background.default};
/* animation */
transition: transform 0.1s linear;
&:hover {
background-color: ${({ theme }) =>
theme.theme.component.ui.color.background.hover};
}
`
export default styled.div`
${HeaderButtonCSS}
`

View file

@ -2,35 +2,33 @@ import { useContext } from "react"
import styled from "styled-components"
import { Link } from "react-router-dom"
import { StyledLink } from "./Navbar"
import HeaderButton from "./HeaderButton"
import NavbarData from "../../data/NavbarData"
import theming from "../../styles/theming"
import { globalContext } from "../../globalContext"
const StyledNavLinks = styled.div`
const Nav = styled.div`
display: flex;
height: 100%;
@media only screen and (max-width: ${theming.size.screen_size1}) {
@media only screen and (max-width: ${({ theme }) =>
theme.theme.maxDisplayWidth.mobile}) {
display: none;
}
`
const NavLinks = () => {
export default () => {
const { globalState } = useContext(globalContext)
return (
<StyledNavLinks>
<Nav>
{NavbarData.map((item, index) => (
<Link key={index} to={globalState.locale + item.path}>
<StyledLink>
<HeaderButton>
{globalState.locale == "en" ? item.title_en : item.title_kr}
</StyledLink>
</HeaderButton>
</Link>
))}
</StyledNavLinks>
</Nav>
)
}
export default NavLinks

View file

@ -2,24 +2,16 @@ import { useCallback, useEffect, useState } from "react"
import { useLocation } from "react-router-dom"
import styled from "styled-components"
import theming from "../../styles/theming"
const StyledReadProgressBackground = styled.div`
const Background = styled.div`
height: 0.2rem;
background-color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: "silver",
dark: "darkslategrey",
})};
background-color: ${({ theme }) =>
theme.theme.component.scrollProgressBar.color.background};
`
const StyledReadProgress = styled.div`
const ProgressBar = styled.div`
height: 100%;
background-color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: theming.light.color0,
dark: theming.dark.color2,
})};
background-color: ${({ theme }) =>
theme.theme.component.scrollProgressBar.color.foreground};
`
const st = "scrollTop"
@ -58,9 +50,9 @@ const ReadProgress = () => {
}, [location])
return (
<StyledReadProgressBackground>
<StyledReadProgress style={{ width: `${scroll}%` }} />
</StyledReadProgressBackground>
<Background>
<ProgressBar style={{ width: `${scroll}%` }} />
</Background>
)
}

View file

@ -0,0 +1,3 @@
import Navbar from "./Header"
export default Navbar

View file

@ -6,8 +6,6 @@ import styled from "styled-components"
import MainContent from "./MainContent"
import theming from "../styles/theming"
const StyledContainer = styled(MainContent)`
display: flex;
flex-direction: column;
@ -30,11 +28,7 @@ const StyledContainer = styled(MainContent)`
`
const StyledSVG = styled.svg`
--color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: theming.light.color1,
dark: theming.dark.color1,
})};
--color: ${({ theme }) => theme.theme.color.text.default};
display: block;
margin: 1rem;

View file

@ -1,5 +1,4 @@
import styled, { css } from "styled-components"
import theming from "../styles/theming"
import Card from "./Card"
@ -15,7 +14,8 @@ export const mainContentCSS = css`
max-width: fit-content;
}
@media screen and (max-width: ${theming.size.screen_size1}) {
@media screen and (max-width: ${({ theme }) =>
theme.theme.maxDisplayWidth.mobile}) {
width: auto;
margin: 1rem;
}

View file

@ -1,95 +0,0 @@
import { useContext } from "react"
import styled from "styled-components"
import { Link } from "react-router-dom"
import NavLinks from "./NavLinks"
import LocaleToggleButton from "./LocaleToggleButton"
import ThemeToggleButton from "./ThemeToggleButton"
import SearchButton from "./SearchButton"
import ReadProgress from "./ReadProgress"
import Sidebar from "../Sidebar"
import theming from "../../styles/theming"
import { globalContext } from "../../globalContext"
const StyledNav = styled.nav`
/* set z index to arbitrarily high value to prevent other components from drawing over the navbar */
z-index: 9999;
position: fixed;
width: 100%;
background-color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: theming.light.backgroundColor0,
dark: theming.dark.backgroundColor0,
})};
color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: theming.light.color0,
dark: theming.dark.color0,
})};
box-shadow: 0 4px 10px rgb(0 0 0 / 5%);
`
const StyledContainer = styled.div`
margin: 0 auto;
align-items: center;
display: flex;
height: 4rem;
/* account for 20px scrollbar width */
@media only screen and (min-width: calc(${theming.size
.screen_size2} + 20px)) {
width: calc(${theming.size.screen_size2} - 20px);
}
`
const RightButtons = styled.div`
display: flex;
height: 100%;
margin-left: auto;
`
const StyledImg = styled.img`
height: 2.5rem;
display: block;
margin: 1rem;
`
export const StyledLink = styled.div`
${theming.styles.navbarButtonStyle}
`
const Navbar = () => {
const { globalState } = useContext(globalContext)
const { locale } = globalState
return (
<StyledNav>
<StyledContainer>
{/* icon */}
<Link to={`/${locale}`}>
<StyledImg src="/icon/icon_circle.svg" />
</Link>
{/* nav links */}
<NavLinks />
{/* right buttons */}
<RightButtons>
<LocaleToggleButton />
<ThemeToggleButton />
<SearchButton />
</RightButtons>
{/* etc */}
<Sidebar />
</StyledContainer>
<ReadProgress />
</StyledNav>
)
}
export default Navbar

View file

@ -1,3 +0,0 @@
import Navbar from "./Navbar"
export default Navbar

View file

@ -15,57 +15,41 @@ import Tag from "./Tag"
import TagList from "./TagList"
import MainContent from "./MainContent"
import theming from "../styles/theming"
import { globalContext } from "../globalContext"
const PostCard = styled(MainContent)`
box-shadow: 0 4px 10px rgb(0 0 0 / 10%);
text-align: left;
margin-bottom: 2rem;
:hover {
cursor: pointer;
box-shadow: 0 4px 10px
${({ theme }) => theme.theme.component.card.color.hoverGlow};
}
`
const PostCardContainer = styled(Link)`
display: block;
padding: 2rem;
text-decoration: none;
padding: 0;
color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: theming.light.color2,
dark: theming.dark.color2,
})};
/* override link color */
color: ${({ theme }) => theme.theme.color.text.gray};
&:hover {
color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: theming.light.color2,
dark: theming.dark.color2,
})};
color: ${({ theme }) => theme.theme.color.text.gray};
}
`
const StyledPostCard = styled(MainContent)`
box-shadow: 0 4px 10px rgb(0 0 0 / 10%);
text-align: left;
margin-bottom: 2rem;
color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: theming.light.color1,
dark: theming.dark.color1,
})};
${theming.styles.hoverCard}
`
const StyledTitle = styled.h1`
const Title = styled.h1`
font-size: 2rem;
font-style: bold;
margin: 0;
margin-bottom: 1rem;
color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: theming.light.color2,
dark: theming.dark.color2,
})};
`
const StyledMetaContainer = styled.small``
const MetaContainer = styled.small``
interface PostCardData extends PostData {
content_id: string
@ -75,26 +59,26 @@ interface Props {
postData: PostCardData
}
const PostCard = (props: Props) => {
export default (props: Props) => {
const { postData } = props
const { content_id, wordCount, date, readTime, title, tags } = postData
const { globalState } = useContext(globalContext)
return (
<StyledPostCard>
<PostCard>
<PostCardContainer
to={`/${globalState.locale}${content_id.replace(/(.kr)$/g, "")}`}
>
<StyledTitle>
<Title>
{title || "No title"}
{/* show "(series)" for urls that matches regex "/series/<series-title>" */}
{/\/series\/[^/]*$/.test(content_id) && " (series)"}
</StyledTitle>
</Title>
<br />
<StyledMetaContainer>
<MetaContainer>
<TagList direction="left">
{tags &&
tags.map((tag) => {
@ -115,10 +99,8 @@ const PostCard = (props: Props) => {
{typeof wordCount === "number"
? wordCount + " words"
: "unknown length"}
</StyledMetaContainer>
</MetaContainer>
</PostCardContainer>
</StyledPostCard>
</PostCard>
)
}
export default PostCard

View file

@ -9,14 +9,15 @@ import { faEllipsisV, faTimes } from "@fortawesome/free-solid-svg-icons"
import SubMenu from "./SubMenu"
import NavbarData from "../../data/NavbarData"
import theming from "../../styles/theming"
import HeaderButton from "../Header/HeaderButton"
const CommonSidebarToggleButtonStyle = css`
${theming.styles.navbarButtonStyle}
${HeaderButton}
width: 1.5rem;
text-align: center;
cursor: pointer;
@media only screen and (min-width: ${theming.size.screen_size1}) {
@media only screen and (min-width: ${({ theme }) =>
theme.theme.maxDisplayWidth.mobile}) {
display: none;
}
`
@ -65,16 +66,9 @@ const SidebarNav = styled.nav<{ isSidebarOpen: boolean }>`
z-index: 30;
overflow-x: hidden;
overflow-y: scroll;
background-color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: theming.light.backgroundColor0,
dark: theming.dark.backgroundColor0,
})};
color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: theming.light.color0,
dark: theming.dark.color0,
})};
background-color: ${({ theme }) =>
theme.theme.component.header.color.background};
color: ${({ theme }) => theme.theme.component.header.color.text};
`
const SidebarWrap = styled.div`

View file

@ -2,17 +2,17 @@
* @file Submenu item for sidebar
*/
import type { Item } from "../../data/NavbarData"
import { useCallback, useContext, useState } from "react"
import { Link } from "react-router-dom"
import styled from "styled-components"
import theming from "../../styles/theming"
import { globalContext } from "../../globalContext"
import type { Item } from "../../data/NavbarData"
import button from "../../styles/button"
const SidebarLink = styled(Link)`
${theming.styles.navbarButtonStyle};
${button};
display: flex;
width: 100%;
margin: 0;

View file

@ -4,19 +4,13 @@ import styled from "styled-components"
import { faHashtag } from "@fortawesome/free-solid-svg-icons"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import theming from "../styles/theming"
const StyledTag = styled.div`
const Tag = styled.div`
text-align: center;
margin-right: 0.8rem;
border-radius: 10px;
color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: theming.light.color2,
dark: theming.dark.color2,
})};
color: ${({ theme }) => theme.theme.color.text.gray};
`
interface Props {
@ -24,12 +18,10 @@ interface Props {
onClick?: (event: MouseEvent<never>) => void
}
const Tag = (props: Props) => {
export default (props: Props) => {
return (
<StyledTag onClick={props.onClick || undefined}>
<Tag onClick={props.onClick || undefined}>
<FontAwesomeIcon icon={faHashtag} /> &nbsp;{props.text}
</StyledTag>
</Tag>
)
}
export default Tag

View file

@ -1,8 +1,11 @@
import type { Dispatch, ReactNode, ReactElement } from "react"
import type { Theme } from "@developomp-site/theme"
import darkTheme from "@developomp-site/theme/dist/dark.json"
import lightTheme from "@developomp-site/theme/dist/light.json"
import { createContext, useEffect, useReducer } from "react"
import storage from "local-storage-fallback"
import type { Dispatch, ReactNode, ReactElement } from "react"
export type SiteLocale = "en" | "kr"
export type SiteTheme = "dark" | "light"
@ -11,11 +14,6 @@ export enum ActionsEnum {
UPDATE_LOCALE,
}
export interface IGlobalState {
locale: SiteLocale
theme: SiteTheme
}
export type GlobalAction =
| {
type: ActionsEnum.UPDATE_THEME
@ -26,6 +24,12 @@ export type GlobalAction =
payload: SiteLocale
}
export interface IGlobalState {
locale: SiteLocale
currentTheme: SiteTheme
theme: Theme
}
export interface IGlobalContext {
globalState: IGlobalState
dispatch: Dispatch<GlobalAction>
@ -40,7 +44,11 @@ function getDefaultLocale(): SiteLocale {
const defaultState: IGlobalState = {
locale: getDefaultLocale(),
theme: (storage.getItem("theme") || "dark") as SiteTheme,
currentTheme: (storage.getItem("theme") || "dark") as SiteTheme,
theme:
((storage.getItem("theme") || "dark") as SiteTheme) === "dark"
? darkTheme
: lightTheme,
}
export const globalContext = createContext({} as IGlobalContext)
@ -48,7 +56,8 @@ export const globalContext = createContext({} as IGlobalContext)
function reducer(state = defaultState, action: GlobalAction): IGlobalState {
switch (action.type) {
case ActionsEnum.UPDATE_THEME:
state.theme = action.payload
state.currentTheme = action.payload
state.theme = state.currentTheme === "dark" ? darkTheme : lightTheme
break
case ActionsEnum.UPDATE_LOCALE:
@ -67,8 +76,8 @@ export function GlobalStore(props: { children: ReactNode }): ReactElement {
// save theme when it is changed
useEffect(() => {
storage.setItem("theme", globalState.theme)
}, [globalState.theme])
storage.setItem("theme", globalState.currentTheme)
}, [globalState.currentTheme])
// save locale when it is changed
useEffect(() => {

View file

@ -2,8 +2,9 @@
* PostList.tsx
* show posts in recent order
*/
import type { Map } from "../../../types/types"
import { useContext, useEffect, useState } from "react"
import { useCallback, useContext, useEffect, useState } from "react"
import { Helmet } from "react-helmet-async"
import styled from "styled-components"
@ -11,25 +12,20 @@ import PostCard from "../../components/PostCard"
import ShowMoreButton from "./ShowMoreButton"
import _map from "../../data/map.json"
import theming from "../../styles/theming"
import { globalContext } from "../../globalContext"
import type { Map } from "../../../types/types"
const map: Map = _map
const StyledPostList = styled.div`
const PostList = styled.div`
flex-direction: column;
align-items: center;
text-align: center;
color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: "#111111",
dark: "#EEEEEE",
})};
color: ${({ theme }) => theme.theme.color.text.default};
`
const Home = () => {
export default () => {
const { globalState } = useContext(globalContext)
const { locale } = globalState
@ -37,7 +33,7 @@ const Home = () => {
const [postsLength, setPostsLength] = useState(0)
const [postCards, setPostCards] = useState<JSX.Element[]>([])
function loadPostCards() {
const loadPostCards = useCallback(() => {
let postCount = 0
const postCards = [] as JSX.Element[]
@ -65,7 +61,7 @@ const Home = () => {
}
setPostCards(postCards)
}
}, [howMany, postCards])
useEffect(() => {
loadPostCards()
@ -81,9 +77,11 @@ const Home = () => {
<meta property="og:image" content="/icon/icon.svg" />
</Helmet>
<StyledPostList>
<PostList>
<h1>{locale == "en" ? "Recent Posts" : "최근 포스트"}</h1>
{postCards}
{postsLength > howMany && (
<ShowMoreButton
action={() => {
@ -91,9 +89,7 @@ const Home = () => {
}}
/>
)}
</StyledPostList>
</PostList>
</>
)
}
export default Home

View file

@ -1,54 +1,21 @@
import { useContext } from "react"
import styled from "styled-components"
import theming from "../../styles/theming"
import { globalContext } from "../../globalContext"
import buttonStyle from "../../styles/button"
const Button = styled.button`
/* size */
${buttonStyle}
padding: 1rem;
/* styling */
display: inline-block;
border: none;
cursor: pointer;
border-radius: 0.5rem;
/* text */
text-align: center;
text-decoration: none;
font-size: 1rem;
/* colors */
color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: "black",
dark: "#CFD0D0",
})};
background-color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: theming.light.backgroundColor2,
dark: theming.dark.backgroundColor2,
})};
:hover {
background-color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: theming.light.backgroundColor0,
dark: theming.dark.backgroundColor0,
})};
}
/* center div */
margin: 0 auto;
`
interface Props {
action(): void
}
const ShowMoreButton = (props: Props) => {
export default (props: Props) => {
const { globalState } = useContext(globalContext)
return (
@ -57,5 +24,3 @@ const ShowMoreButton = (props: Props) => {
</Button>
)
}
export default ShowMoreButton

View file

@ -8,14 +8,9 @@ import {
} from "@fortawesome/free-solid-svg-icons"
import { PageData } from "../../../types/types"
import theming from "../../styles/theming"
const StyledMetaContainer = styled.div`
color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: "#555",
dark: "#CCC",
})};
color: ${({ theme }) => theme.theme.color.text.gray};
`
const Meta = (props: { fetchedPage: PageData }) => {

View file

@ -1,5 +1,5 @@
import { useContext } from "react"
import styled, { css } from "styled-components"
import styled from "styled-components"
import { Link } from "react-router-dom"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
@ -9,8 +9,7 @@ import {
faListUl,
} from "@fortawesome/free-solid-svg-icons"
import theming from "../../styles/theming"
import buttonStyle from "../../styles/button"
import { globalContext } from "../../globalContext"
const Container = styled.div`
@ -18,26 +17,6 @@ const Container = styled.div`
justify-content: space-between;
`
const buttonStyle = css`
${theming.styles.navbarButtonStyle}
background-color: ${(props) =>
theming.theme(props.theme.currentTheme, {
dark: "#202225",
light: "#EEEEEE",
})};
border-radius: 0.5rem;
height: 3rem;
&:hover {
background-color: ${(props) =>
theming.theme(props.theme.currentTheme, {
dark: theming.dark.backgroundColor1,
light: theming.light.backgroundColor2,
})};
}
`
const Button = styled.div`
${buttonStyle}
`
@ -47,14 +26,6 @@ const DisabledButton = styled.div`
color: grey;
cursor: default;
&:hover {
background-color: ${(props) =>
theming.theme(props.theme.currentTheme, {
dark: "#202225",
light: "#EEEEEE",
})};
}
`
interface Props {

View file

@ -6,7 +6,6 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import { faCaretDown, faCaretUp } from "@fortawesome/free-solid-svg-icons"
import styled from "styled-components"
import theming from "../../styles/theming"
import { globalContext } from "../../globalContext"
const StyledTocToggleButton = styled.button`
@ -16,11 +15,7 @@ const StyledTocToggleButton = styled.button`
background-color: rgba(0, 0, 0, 0);
width: 100%;
padding: 0.5rem;
color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: "black",
dark: "white",
})};
color: ${({ theme }) => theme.theme.color.text.highContrast};
`
const StyledCollapseContainer = styled.div`

View file

@ -6,21 +6,21 @@ import Badge from "../../components/Badge"
import { cardCSS } from "../../components/Card"
import { PortfolioProject } from "../../../types/types"
import theming from "../../styles/theming"
import { globalContext } from "../../globalContext"
const StyledProjectCard = styled.div`
${cardCSS}
${theming.styles.hoverCard}
color: ${(props) => props.theme.theme.color.text.default};
margin-bottom: 2rem;
word-wrap: break-word;
color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: theming.light.color1,
dark: theming.dark.color1,
})};
:hover {
cursor: pointer;
box-shadow: 0 4px 10px
${(props) => props.theme.theme.component.card.color.hoverGlow};
}
`
const StyledImg = styled.img`

View file

@ -1,12 +1,11 @@
import { DateRange } from "react-date-range"
import styled from "styled-components"
import theming from "../../styles/theming"
export const DateRangeControl = styled.div`
width: 350px;
@media screen and (max-width: ${theming.size.screen_size2}) {
@media screen and (max-width: ${(props) =>
props.theme.theme.maxDisplayWidth.mobile}) {
margin-top: 2rem;
}
`

View file

@ -1,6 +1,4 @@
/* eslint-disable react/prop-types */
import { useContext, useEffect, useState } from "react"
import { useCallback, useContext, useEffect, useState } from "react"
import styled from "styled-components"
import { useSearchParams } from "react-router-dom"
import { Helmet } from "react-helmet-async"
@ -10,7 +8,6 @@ import elasticlunr from "elasticlunr" // search engine
import _map from "../../data/map.json"
import searchData from "../../data/search.json"
import theming from "../../styles/theming"
import Loading from "../../components/Loading"
import PostCard from "../../components/PostCard"
@ -54,7 +51,8 @@ const StyledSearchContainer = styled.div`
display: flex;
align-items: flex-start;
@media screen and (max-width: ${theming.size.screen_size2}) {
@media screen and (max-width: ${(props) =>
props.theme.theme.maxDisplayWidth.mobile}) {
flex-direction: column-reverse;
align-items: center;
}
@ -64,7 +62,8 @@ const StyledSearchControlContainer = styled.div`
width: 100%;
margin-left: 1rem;
@media screen and (max-width: ${theming.size.screen_size2}) {
@media screen and (max-width: ${(props) =>
props.theme.theme.maxDisplayWidth.mobile}) {
margin-top: 2rem;
margin-left: 0;
}
@ -113,6 +112,38 @@ const Search = () => {
const [postCards, setPostCards] = useState<JSX.Element[]>([])
const doSearch = useCallback(() => {
try {
const _postCards: JSX.Element[] = []
for (const res of searchIndex.search(searchInput)) {
const postData = map.posts[res.ref]
if (
postData && // if post data exists
isDateInRange(postData.date, dateRange[0]) && // date is within range
isSelectedTagsInPost(selectedTags, postData.tags) // if post include tags
) {
_postCards.push(
<PostCard
key={res.ref}
postData={{
content_id: res.ref,
...postData,
}}
/>
)
}
}
// apply search result
setPostCards(_postCards)
// eslint-disable-next-line no-empty
} catch (err) {
console.error(err)
}
}, [dateRange, selectedTags, searchInput])
// parse search parameters
useEffect(() => {
for (const [key, value] of URLSearchParams.entries()) {
@ -186,38 +217,6 @@ const Search = () => {
return () => clearTimeout(delayDebounceFn)
}, [searchInput])
function doSearch() {
try {
const _postCards: JSX.Element[] = []
for (const res of searchIndex.search(searchInput)) {
const postData = map.posts[res.ref]
if (
postData && // if post data exists
isDateInRange(postData.date, dateRange[0]) && // date is within range
isSelectedTagsInPost(selectedTags, postData.tags) // if post include tags
) {
_postCards.push(
<PostCard
key={res.ref}
postData={{
content_id: res.ref,
...postData,
}}
/>
)
}
}
// apply search result
setPostCards(_postCards)
// eslint-disable-next-line no-empty
} catch (err) {
console.error(err)
}
}
if (!initialized) return <Loading />
return (

View file

@ -1,32 +1,30 @@
import styled from "styled-components"
import theming from "../../styles/theming"
const StyledSearchBar = styled.input`
export default styled.input`
width: 100%;
border-radius: 100px; /* arbitrarily large value */
height: 2.5rem;
text-align: center;
font-size: 1.2rem;
outline: none;
border: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: "1px solid #ccc",
dark: "1px solid #555",
})};
color: ${({ theme }) => theme.theme.color.text.default};
border: 1px solid
${(props) => props.theme.theme.component.input.color.border.default};
background-color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: theming.dark.color1,
dark: theming.dark.backgroundColor1,
})};
props.theme.theme.component.input.color.background.default};
color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: theming.light.color1,
dark: theming.dark.color1,
})};
::placeholder {
color: ${(props) => props.theme.theme.component.input.color.placeHolder};
opacity: 1;
}
&:hover {
border: 1px solid
${({ theme }) => theme.theme.component.input.color.border.hover};
}
&:focus {
border: 1px solid
${({ theme }) => theme.theme.component.input.color.border.focus};
}
`
export default StyledSearchBar

View file

@ -2,8 +2,6 @@ import { useContext } from "react"
import styled from "styled-components"
import Select from "react-select"
import theming from "../../styles/theming"
import _map from "../../data/map.json"
import { globalContext } from "../../globalContext"
@ -33,24 +31,19 @@ interface TagSelectProps {
const TagSelect = (props: TagSelectProps) => {
const { globalState } = useContext(globalContext)
const { theme } = globalState
const locale = globalState.locale
const currentTheme = globalState.theme
const { onChange, defaultValue: selectedTags } = props
return (
<StyledReactTagsContainer>
<Select
placeholder={locale == "en" ? "Select tags..." : "태그를 선택하세요"}
theme={(theme) => ({
...theme,
theme={(reactSelectTheme) => ({
...reactSelectTheme,
colors: {
...theme.colors,
neutral0: theming
.theme(currentTheme, {
light: theming.light.backgroundColor1,
dark: theming.dark.backgroundColor1,
})
.toString(),
...reactSelectTheme.colors,
neutral0: theme.component.input.color.background.default,
neutral5: "hsl(0, 0%, 20%)",
neutral10: "hsl(0, 0%, 30%)",
neutral20: "hsl(0, 0%, 40%)",
@ -68,59 +61,42 @@ const TagSelect = (props: TagSelectProps) => {
styles={{
option: (styles) => ({
...styles,
backgroundColor: theming
.theme(currentTheme, {
light: theming.light.backgroundColor1,
dark: theming.dark.backgroundColor1,
})
.toString(),
color: theming
.theme(currentTheme, {
light: theming.light.color1,
dark: theming.dark.color1,
})
.toString(),
backgroundColor: theme.component.input.color.background.default,
color: theme.color.text.default,
cursor: "pointer",
padding: 10,
":hover": {
backgroundColor: theming
.theme(currentTheme, {
light: theming.light.backgroundColor0,
dark: theming.dark.backgroundColor0,
})
.toString(),
backgroundColor: theme.component.input.color.background.itemHover,
},
}),
control: (styles) => ({
...styles,
backgroundColor: theming
.theme(currentTheme, {
light: theming.light.backgroundColor1,
dark: theming.dark.backgroundColor1,
})
.toString(),
border: theming.theme(currentTheme, {
light: "1px solid #ccc",
dark: "1px solid #555",
}),
backgroundColor: theme.component.input.color.background.default,
border: `1px solid ${theme.component.input.color.border.default}`,
":hover": {
border: `1px solid ${theme.component.input.color.border.hover}`,
},
":focus": {
border: `1px solid ${theme.component.input.color.border.focus}`,
},
}),
multiValue: (styles) => ({
...styles,
color: "white",
backgroundColor: theming.light.linkColor,
borderRadius: "5px",
color: theme.color.text.default,
backgroundColor: theme.component.ui.color.background.default,
borderRadius: "10px",
}),
multiValueLabel: (styles) => ({
...styles,
color: theme.color.text.default,
marginLeft: "0.2rem",
marginRight: "0.2rem",
}),
multiValueRemove: (styles) => ({
...styles,
marginLeft: "0.2rem",
marginRight: "0.3rem",
cursor: "pointer",
color: theme.component.input.color.placeHolder,
":hover": {
backgroundColor: "white",
color: theming.light.linkColor,
color: theme.color.text.default,
},
}),
}}

View file

@ -0,0 +1,32 @@
import { css } from "styled-components"
export default css`
a {
text-decoration: none;
color: ${(props) => props.theme.theme.component.anchor.color.default};
&:hover {
color: ${(props) => props.theme.theme.component.anchor.color.hover};
}
&:active {
color: ${(props) => props.theme.theme.component.anchor.color.active};
}
}
/* The "#" thingy used beside headers */
a.header-anchor {
/* compensate for navbar height*/
display: inline-block;
margin-top: 4.5rem;
color: ${(props) => props.theme.theme.component.anchor.color.header};
}
/* footnote anchors */
a[id^="fnref"] {
display: inline;
padding-top: 4.5rem;
}
`

View file

@ -0,0 +1,19 @@
import { css } from "styled-components"
export default css`
blockquote {
background-color: ${({ theme }) =>
theme.theme.component.blockQuote.color.background};
border-left: 0.4rem solid
${({ theme }) => theme.theme.component.blockQuote.color.borderLeft};
padding-top: 0.1rem;
padding-right: 1rem;
padding-bottom: 0.1rem;
padding-left: 1.5rem;
@media screen and (max-width: ${({ theme }) =>
theme.theme.maxDisplayWidth.mobile}) {
margin: 0.5rem;
}
}
`

View file

@ -0,0 +1,37 @@
import { css } from "styled-components"
export default css`
/* style */
display: flex;
cursor: pointer;
align-items: center;
justify-content: center;
border: none;
border-radius: 0.5rem;
/* size */
height: 3rem;
min-width: 2.5rem;
margin: 0;
padding: 0 1rem 0 1rem;
/* text */
text-decoration: none;
/* color */
color: ${({ theme }) => theme.theme.color.text.default};
background-color: ${({ theme }) =>
theme.theme.component.ui.color.background.default};
&:hover {
background-color: ${({ theme }) =>
theme.theme.component.ui.color.background.hover};
}
/* animation */
transition: transform 0.1s linear;
`

View file

@ -0,0 +1,37 @@
import { css } from "styled-components"
export default css`
/* highlight.js code style */
${({ theme }) => theme.theme.component.code.block.style}
/* inline code */
:not(pre) > code {
font-family: ${({ theme }) => theme.theme.font.monospace};
word-wrap: break-word;
color: ${({ theme }) => theme.theme.component.code.inline.color.text};
background-color: ${({ theme }) =>
theme.theme.component.code.inline.color.background};
border: 1px solid
${({ theme }) => theme.theme.component.code.inline.color.border};
border-radius: 3px;
padding: 0 3px;
}
/* code block */
pre > code {
font-family: ${(props) => props.theme.theme.font.monospace};
border: 1px solid
${({ theme }) => theme.theme.component.code.block.color.border};
}
/* // todo: fix highlight not working properly when scrolled horizontally // */
.highlighted-line {
background-color: ${({ theme }) =>
theme.theme.component.code.block.color.highlight};
display: block;
min-width: min-content;
margin: 0 -1rem;
padding: 0 1rem;
}
`

View file

@ -1,102 +0,0 @@
/**
* from node_modules/highlight.js/styles/atom-one-dark-reasonable.css
*/
import { css } from "styled-components"
export default css`
pre code.hljs {
display: block;
overflow-x: auto;
padding: 1em;
}
code.hljs {
padding: 3px 5px;
}
.hljs {
color: #abb2bf;
background: #282c34;
}
.hljs-keyword,
.hljs-operator,
.hljs-pattern-match {
color: #f92672;
}
.hljs-function,
.hljs-pattern-match .hljs-constructor {
color: #61aeee;
}
.hljs-function .hljs-params {
color: #a6e22e;
}
.hljs-function .hljs-params .hljs-typing {
color: #fd971f;
}
.hljs-module-access .hljs-module {
color: #7e57c2;
}
.hljs-constructor {
color: #e2b93d;
}
.hljs-constructor .hljs-string {
color: #9ccc65;
}
.hljs-comment,
.hljs-quote {
color: #b18eb1;
font-style: italic;
}
.hljs-doctag,
.hljs-formula {
color: #c678dd;
}
.hljs-deletion,
.hljs-name,
.hljs-section,
.hljs-selector-tag,
.hljs-subst {
color: #e06c75;
}
.hljs-literal {
color: #56b6c2;
}
.hljs-addition,
.hljs-attribute,
.hljs-meta .hljs-string,
.hljs-regexp,
.hljs-string {
color: #98c379;
}
.hljs-built_in,
.hljs-class .hljs-title,
.hljs-title.class_ {
color: #e6c07b;
}
.hljs-attr,
.hljs-number,
.hljs-selector-attr,
.hljs-selector-class,
.hljs-selector-pseudo,
.hljs-template-variable,
.hljs-type,
.hljs-variable {
color: #d19a66;
}
.hljs-bullet,
.hljs-link,
.hljs-meta,
.hljs-selector-id,
.hljs-symbol,
.hljs-title {
color: #61aeee;
}
.hljs-emphasis {
font-style: italic;
}
.hljs-strong {
font-weight: 700;
}
.hljs-link {
text-decoration: underline;
}
`

View file

@ -1,85 +0,0 @@
/**
* from node_modules/highlight.js/styles/default.css
*/
import { css } from "styled-components"
export default css`
pre code.hljs {
display: block;
overflow-x: auto;
padding: 1em;
}
code.hljs {
padding: 3px 5px;
}
.hljs {
background: #f0f0f0;
color: #444;
}
.hljs-comment {
color: #888;
}
.hljs-punctuation,
.hljs-tag {
color: #444a;
}
.hljs-tag .hljs-attr,
.hljs-tag .hljs-name {
color: #444;
}
.hljs-attribute,
.hljs-doctag,
.hljs-keyword,
.hljs-meta .hljs-keyword,
.hljs-name,
.hljs-selector-tag {
font-weight: 700;
}
.hljs-deletion,
.hljs-number,
.hljs-quote,
.hljs-selector-class,
.hljs-selector-id,
.hljs-string,
.hljs-template-tag,
.hljs-type {
color: #800;
}
.hljs-section,
.hljs-title {
color: #800;
font-weight: 700;
}
.hljs-link,
.hljs-operator,
.hljs-regexp,
.hljs-selector-attr,
.hljs-selector-pseudo,
.hljs-symbol,
.hljs-template-variable,
.hljs-variable {
color: #bc6060;
}
.hljs-literal {
color: #78a960;
}
.hljs-addition,
.hljs-built_in,
.hljs-bullet,
.hljs-code {
color: #397300;
}
.hljs-meta {
color: #1f7199;
}
.hljs-meta .hljs-string {
color: #4d99bf;
}
.hljs-emphasis {
font-style: italic;
}
.hljs-strong {
font-weight: 700;
}
`

View file

@ -1,285 +1,16 @@
import { createGlobalStyle, css } from "styled-components"
import codeblockLightCSS from "./codeblock-light"
import codeblockDarkCSS from "./codeblock-dark"
import "katex/dist/katex.min.css"
import theming from "./theming"
const anchorCSS = css`
a {
text-decoration: none;
color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: theming.light.linkColor,
dark: theming.dark.linkColor,
})};
&:hover {
color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: theming.dark.linkColor,
dark: theming.light.linkColor,
})};
}
}
/* The "#" thingy used beside headers */
a.header-anchor {
/* compensate for navbar height*/
display: inline-block;
margin-top: 4.5rem;
color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: "lightgray",
dark: "lightslategray",
})};
}
/* footnote anchors */
a[id^="fnref"] {
display: inline;
padding-top: 4.5rem;
}
`
const scrollbarCSS = css`
body::-webkit-scrollbar {
width: 8px;
}
body::-webkit-scrollbar-track {
border-radius: ${theming.size.x2_small};
background: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: theming.light.backgroundColor0,
dark: theming.dark.backgroundColor0,
})};
box-shadow: inset 0 0 5px rgb(0 0 0 / 10%);
}
body::-webkit-scrollbar-thumb {
border-radius: ${theming.size.x2_small};
background: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: theming.light.backgroundColor2,
dark: "#888888",
})};
box-shadow: inset 0 0 10px rgb(0 0 0 / 20%);
}
`
const codeCSS = css`
${(props) => {
switch (props.theme.currentTheme) {
case "dark":
return codeblockDarkCSS
case "light":
return codeblockLightCSS
default:
return codeblockDarkCSS
}
}}
/* line code */
:not(pre) > code {
font-family: ${theming.font.code};
word-wrap: break-word;
color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: theming.light.color1,
dark: theming.dark.color1,
})};
background-color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: "#eee",
dark: "#444", // I hope no hardcore christian finds this code
})};
border: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: "1px solid #BBB",
dark: "1px solid #666", // especially this
})};
border-radius: 3px;
padding: 0 3px;
}
/* code block */
pre > code {
font-family: ${theming.font.code};
border: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: "1px solid #BBB",
dark: "1px solid #555",
})};
}
/* // todo: fix highlight not working properly when scrolled horizontally // */
.highlighted-line {
background-color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: "#dddddd",
dark: "#14161a",
})};
display: block;
min-width: min-content;
margin: 0 -1rem;
padding: 0 1rem;
}
`
const kbdCSS = css`
/* https://www.rgagnon.com/jsdetails/js-nice-effect-the-KBD-tag.html */
kbd {
margin: 0px 0.1em;
padding: 0.1em 0.6em;
border-radius: 3px;
border: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: "1px solid #CCCCCC",
dark: "1px solid #555555",
})};
color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: "#333333",
dark: "white",
})};
line-height: 1.4;
font-size: 13.5px;
display: inline-block;
box-shadow: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: "0px 1px 0px rgba(0,0,0,0.2), inset 0px 0px 0px 2px white",
dark: "0px 1px 0px rgba(255,255,255,0.3), inset 0px 0px 0px 2px black",
})};
background-color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: "#F7F7F7",
dark: "black",
})};
}
`
const tableCSS = css`
table {
border-collapse: collapse;
border-spacing: 0;
width: 100%;
td,
th {
padding: 8px;
border: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: "1px solid #ddd",
dark: "1px solid #777777",
})};
}
/* table alternating color */
tr:nth-child(even) {
background-color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: "#f2f2f2",
dark: "#21272E",
})};
}
}
`
const blockquoteCSS = css`
blockquote {
background-color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: "rgba(0, 0, 0, 5%)",
dark: "rgba(255, 255, 255, 7%)",
})};
border-left: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: "0.4rem solid rgba(0, 0, 0, 10%)",
dark: "0.4rem solid rgba(255, 255, 255, 30%)",
})};
padding-top: 0.1rem;
padding-right: 1rem;
padding-bottom: 0.1rem;
padding-left: 1.5rem;
@media screen and (max-width: ${theming.size.screen_size1}) {
margin: 0.5rem;
}
}
`
const hrCSS = css`
hr {
border: 0;
border-bottom: 1px solid;
}
`
const headerCSS = css`
/* intentionally left out h1 */
h2,
h3,
h4,
h5,
h6 {
margin-top: -2rem;
margin-bottom: 0.5rem;
font-weight: 700;
}
h1 {
font-size: 2.5rem;
}
h2 {
font-size: 1.5rem;
}
h3 {
font-size: 1rem;
text-indent: 0.5rem;
}
h4 {
font-size: 1rem;
text-indent: 1rem;
}
h5 {
font-size: 1rem;
text-indent: 1.5rem;
}
h6 {
font-size: 1rem;
text-indent: 2rem;
}
`
const markCSS = css`
mark {
background-color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: "rgba(255, 255, 0, 75%)",
dark: "rgba(255, 255, 0, 50%)",
})};
color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: "black",
dark: "white",
})};
}
`
const katexCSS = css`
// prevent overflowing on small displays
.katex-html {
overflow: auto;
padding: 0.5rem;
}
`
import anchorCSS from "./anchor"
import scrollbarCSS from "./scrollbar"
import codeCSS from "./code"
import kbdCSS from "./kbd"
import tableCSS from "./table"
import blockquoteCSS from "./blockQuote"
import hrCSS from "./hr"
import headerCSS from "./header"
import markCSS from "./mark"
import katexCSS from "./katex"
const globalCSS = css`
body {
@ -303,24 +34,16 @@ const globalCSS = css`
/* text */
line-height: 2rem;
font-size: ${theming.size.medium};
font-family: ${theming.font.regular};
font-size: 1rem;
font-family: ${({ theme }) => theme.theme.font.sansSerif};
font-weight: 400;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
/* color */
background-color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: theming.light.backgroundColor1,
dark: theming.dark.backgroundColor1,
})};
color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: theming.light.color1,
dark: theming.dark.color1,
})};
background-color: ${({ theme }) => theme.theme.color.background};
color: ${({ theme }) => theme.theme.color.text.default};
}
* {

View file

@ -0,0 +1,37 @@
import { css } from "styled-components"
export default css`
/* intentionally left out h1 */
h2,
h3,
h4,
h5,
h6 {
margin-top: -2rem;
margin-bottom: 0.5rem;
font-weight: 700;
}
h1 {
font-size: 2.5rem;
}
h2 {
font-size: 1.5rem;
}
h3 {
font-size: 1rem;
text-indent: 0.5rem;
}
h4 {
font-size: 1rem;
text-indent: 1rem;
}
h5 {
font-size: 1rem;
text-indent: 1.5rem;
}
h6 {
font-size: 1rem;
text-indent: 2rem;
}
`

View file

@ -0,0 +1,8 @@
import { css } from "styled-components"
export default css`
hr {
border: 0;
border-bottom: 1px solid;
}
`

View file

@ -0,0 +1,9 @@
import { css } from "styled-components"
export default css`
// prevent overflowing on small displays
.katex-html {
overflow: auto;
padding: 0.5rem;
}
`

View file

@ -0,0 +1,21 @@
import { css } from "styled-components"
export default css`
/* https://www.rgagnon.com/jsdetails/js-nice-effect-the-KBD-tag.html */
kbd {
margin: 0px 0.1em;
padding: 0.1em 0.6em;
border-radius: 3px;
border: 1px solid ${({ theme }) => theme.theme.component.kbd.color.border};
color: ${({ theme }) => theme.theme.component.kbd.color.text};
line-height: 1.4;
font-size: 13.5px;
display: inline-block;
box-shadow: 0px 1px 0px
${({ theme }) => theme.theme.component.kbd.color.outerShadow},
inset 0px 0px 0px 2px
${({ theme }) => theme.theme.component.kbd.color.innerShadow};
background-color: ${({ theme }) =>
theme.theme.component.kbd.color.background};
}
`

View file

@ -0,0 +1,9 @@
import { css } from "styled-components"
export default css`
mark {
background-color: ${({ theme }) =>
theme.theme.component.mark.color.background};
color: ${({ theme }) => theme.theme.component.mark.color.text};
}
`

View file

@ -0,0 +1,21 @@
import { css } from "styled-components"
export default css`
body::-webkit-scrollbar {
width: ${(props) => props.theme.theme.component.scrollbar.width};
}
body::-webkit-scrollbar-track {
border-radius: ${(props) =>
props.theme.theme.component.scrollbar.borderRadius};
background: ${(props) => props.theme.theme.component.scrollbar.color.track};
box-shadow: inset 0 0 5px rgb(0 0 0 / 10%);
}
body::-webkit-scrollbar-thumb {
border-radius: ${(props) =>
props.theme.theme.component.scrollbar.borderRadius};
background: ${(props) => props.theme.theme.component.scrollbar.color.thumb};
box-shadow: inset 0 0 10px rgb(0 0 0 / 20%);
}
`

View file

@ -0,0 +1,22 @@
import { css } from "styled-components"
export default css`
table {
border-collapse: collapse;
border-spacing: 0;
width: 100%;
td,
th {
padding: 8px;
border: 1px solid
${({ theme }) => theme.theme.component.table.color.border};
}
/* table alternating color */
tr:nth-child(even) {
background-color: ${({ theme }) =>
theme.theme.component.table.color.even};
}
}
`

View file

@ -1,107 +0,0 @@
/** theming.ts
* stores values that are used across
* It makes changing values easier
*/
import { css } from "styled-components"
// not declared in the export object so the export object can refer to it
function theme(
currentTheme: string,
values: { [key: string]: string | number }
) {
return values[currentTheme]
}
const theming = {
theme: theme,
font: {
regular: "'Noto Sans KR', sans-serif",
code: "'Source Code Pro', monospace",
},
size: {
x2_small: "3px",
small: 0,
medium: "1rem",
large: 0,
x_large: 0,
screen_size1: "1000px",
screen_size2: "1500px",
},
dark: {
backgroundColor0: "#202225",
backgroundColor1: "#36393F",
backgroundColor2: "#2F3136",
color0: "#FFFFFF",
color1: "#EEEEEE",
color2: "#CCC",
linkColor: "#66AAFF",
},
light: {
backgroundColor0: "#FFFFFF",
backgroundColor1: "#F7F7F7",
backgroundColor2: "#DDDDDD",
color0: "#000000",
color1: "#111111",
color2: "#555",
linkColor: "#4592F7",
},
styles: {
hoverCard: css`
:hover {
cursor: pointer;
box-shadow: ${(props) =>
theme(props.theme.currentTheme, {
light: "0 4px 10px rgb(0 0 0 / 25%)",
dark: "0 4px 10px rgb(255 255 255 / 20%)",
})};
}
`,
navbarButtonStyle: css`
/* style */
display: flex;
cursor: pointer;
align-items: center;
justify-content: center;
/* size */
height: 100%;
min-width: 2.5rem;
margin: 0;
padding: 0 1rem 0 1rem;
/* text */
text-decoration: none;
/* color */
color: ${(props) =>
theme(props.theme.currentTheme, {
light: "black",
dark: "#CFD0D0",
})};
background-color: ${(props) =>
theme(props.theme.currentTheme, {
light: "white",
dark: "#202225",
})};
/* animation */
transition: transform 0.1s linear;
&:hover {
background-color: ${(props) =>
theme(props.theme.currentTheme, {
light: "#EEEEEE",
dark: "#36393F",
})};
}
`,
},
}
export default theming

55
apps/blog/src/theme.tsx Normal file
View file

@ -0,0 +1,55 @@
// import "./DarkMode.css"
import { ChangeEventHandler } from "react"
/* NEW (START) */
const setDark = () => {
localStorage.setItem("theme", "dark")
document.documentElement.setAttribute("data-theme", "dark")
}
const setLight = () => {
localStorage.setItem("theme", "light")
document.documentElement.setAttribute("data-theme", "light")
}
const storedTheme = localStorage.getItem("theme")
const prefersDark =
window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches
const defaultDark =
storedTheme === "dark" || (storedTheme === null && prefersDark)
if (defaultDark) {
setDark()
}
const toggleTheme: ChangeEventHandler<HTMLInputElement> = (e) => {
if (e.target.checked) {
setDark()
} else {
setLight()
}
}
/* NEW (END) */
const DarkMode = () => {
return (
<div className="toggle-theme-wrapper">
<span></span>
<label className="toggle-theme" htmlFor="checkbox">
<input
type="checkbox"
id="checkbox"
// NEW
onChange={toggleTheme}
defaultChecked={defaultDark}
/>
<div className="slider round"></div>
</label>
<span>🌒</span>
</div>
)
}
export default DarkMode

View file

@ -1,5 +1,11 @@
{
"compilerOptions": {
"plugins": [
{
"name": "typescript-styled-plugin",
"validate": false
}
],
"target": "es5",
"module": "esnext",
"lib": ["dom", "dom.iterable", "esnext"],

View file

@ -1,9 +1,10 @@
import "styled-components"
import type { SiteTheme } from "../src/globalContext"
import type { Theme } from "@developomp-site/theme"
import { SiteTheme } from "../src/globalContext"
declare module "styled-components" {
export interface DefaultTheme {
currentTheme: SiteTheme
setTheme(setThemeTo: SiteTheme): void
theme: Theme
}
}

View file

@ -1,5 +1,5 @@
import { Meta, ColorPalette, ColorItem } from "@storybook/addon-docs"
import darkTheme from "@developomp-site/theme/dark"
import darkTheme from "@developomp-site/theme/dist/dark.json"
<Meta title="Colors" />