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" />

5
packages/theme/.eslintrc Normal file
View file

@ -0,0 +1,5 @@
{
"env": {
"node": true
}
}

9
packages/theme/build.ts Normal file
View file

@ -0,0 +1,9 @@
import { writeFileSync, mkdirSync, existsSync } from "fs"
import dark from "./src/dark"
import light from "./src/light"
if (!existsSync("dist")) mkdirSync("dist")
writeFileSync("dist/dark.json", JSON.stringify(dark))
writeFileSync("dist/light.json", JSON.stringify(light))

View file

@ -1,62 +0,0 @@
const theme: Theme = {
font: {
sansSerif: "'Noto Sans KR', sans-serif", // https://fonts.google.com/noto/specimen/Noto+Sans+KR
monospace: "'Source Code Pro', monospace",
},
maxDisplayWidth: {
mobile: "1024px", // max-w-screen-lg
desktop: "1536px", // max-w-screen-2xl
},
component: {
scrollbar: {
width: "8px", // w-2
borderRadius: "4px", // rounded
},
header: {
color: {
background: "#202225", // custom
hover: "#3f3f46", // zinc-700
text: "#d4d4d8", // zinc-300
},
height: "16px", // h-4
},
scrollProgressBar: {
color: {
background: "#52525b", // zinc 600
foreground: "#d4d4d8", // zinc-300
},
},
card: {
color: {
background: "#2f3136",
},
},
footer: {
color: {
background: "",
text: "",
},
},
ui: {
color: {
background: {
interactive: "",
},
text: {
on1: "",
link: "#66AAFF",
linkActive: "#4592F7",
},
},
},
},
}
export default theme

143
packages/theme/index.d.ts vendored Normal file
View file

@ -0,0 +1,143 @@
export interface Theme {
font: {
sansSerif: string
monospace: string
}
color: {
text: {
highContrast: string
default: string
gray: string
}
background: string
}
maxDisplayWidth: {
mobile: string
desktop: string
}
component: {
anchor: {
color: {
default: string
hover: string
active: string
header: string
}
}
blockQuote: {
color: {
background: string
borderLeft: string
}
}
card: {
color: {
background: string
hoverGlow: string
}
}
code: {
inline: {
color: {
text: string
background: string
border: string
}
}
block: {
color: {
border: string
highlight: string
}
style: string
}
}
footer: {
color: {
background: string
text: string
}
}
header: {
color: {
background: string
hover: string
text: string
}
height: string
}
input: {
color: {
background: {
default: string
itemHover: string
}
border: {
default: string
hover: string
focus: string
}
placeHolder: string
}
}
kbd: {
color: {
text: string
border: string
outerShadow: string
innerShadow: string
background: string
}
}
mark: {
color: {
text: string
background: string
}
}
scrollbar: {
color: {
track: string
thumb: string
}
width: string
borderRadius: string
}
scrollProgressBar: {
color: {
background: string
foreground: string
}
}
table: {
color: {
border: string
even: string
}
}
ui: {
color: {
background: {
default: string
hover: string
}
border: string
}
}
}
}

View file

@ -1,5 +0,0 @@
import BaseTheme from "../dark"
const theme: Theme = { ...BaseTheme }
export default theme

View file

@ -1,9 +1,22 @@
{
"name": "@developomp-site/theme",
"version": "0.0.0",
"types": "./index.d.ts",
"private": true,
"license": "MIT",
"scripts": {
"dev": "nodemon --ignore dist/ --exec pnpm build",
"build": "npx ts-node ./build.ts",
"clean": "rm -rf dist && rm -rf node_modules"
},
"devDependencies": {
"tailwindcss": "^3.1.4"
"@types/merge-deep": "^3.0.0",
"@types/node": "^18.11.10",
"merge-deep": "^3.0.3",
"nodemon": "^2.0.20",
"tailwindcss": "^3.1.4",
"ts-node": "^10.9.1",
"tsup": "^5.10.1",
"utility-types": "^3.10.0"
}
}

View file

@ -0,0 +1,167 @@
import type { Theme } from "../.."
import { readFileSync } from "fs"
export default {
font: {
sansSerif: "'Noto Sans KR', sans-serif", // https://fonts.google.com/noto/specimen/Noto+Sans+KR
monospace: "'Source Code Pro', monospace",
},
color: {
text: {
highContrast: "#FFFFFF",
default: "#EEEEEE",
gray: "#CCC",
},
background: "#36393F",
},
maxDisplayWidth: {
mobile: "1024px", // max-w-screen-lg
desktop: "1536px", // max-w-screen-2xl
},
component: {
anchor: {
color: {
default: "#66AAFF",
hover: "#4592F7",
active: "#4592F7",
header: "#778899",
},
},
blockQuote: {
color: {
background: "#FFFFFF12",
borderLeft: "#FFFFFF4D",
},
},
card: {
color: {
background: "#2F3136",
hoverGlow: "#FFFFFF33",
},
},
code: {
inline: {
color: {
text: "#FFFFFF",
background: "#444",
border: "#666",
},
},
block: {
color: {
border: "#555",
highlight: "#14161A",
},
style: readFileSync(__dirname + "/codeblock.css", "utf-8"),
},
},
footer: {
color: {
background: "#000000",
text: "",
},
},
header: {
color: {
background: "#202225", // custom
hover: "#3F3F46", // zinc-700
text: "#D4D4D8", // zinc-300
},
height: "16px", // h-4
},
input: {
color: {
background: {
default: "#36393f",
itemHover: "#202225",
},
border: {
default: "#555555",
hover: "#808080",
focus: "#a3a3a3", // neutral-400
},
placeHolder: "#A9A9A9",
},
},
kbd: {
color: {
text: "#FFFFFF",
border: "#555555",
outerShadow: "#FFFFFF4D",
innerShadow: "#000000",
background: "#000000",
},
},
mark: {
color: {
text: "#FFFFFF",
background: "#FFFF0080",
},
},
scrollbar: {
color: {
track: "#18181B",
thumb: "#888888",
},
width: "8px", // w-2
borderRadius: "4px", // rounded
},
scrollProgressBar: {
color: {
background: "#52525B", // zinc 600
foreground: "#D4D4D8", // zinc-300
},
},
table: {
color: {
border: "#777777",
even: "#21272E",
},
},
ui: {
color: {
background: {
default: "#202225",
hover: "#3F3F46", // zinc-700
},
border: "#555",
},
},
},
} as Theme
/*
dark: {
backgroundColor0: "#18181b",
backgroundColor1: "#36393F",
backgroundColor2: "#2F3136",
color0: "#FFFFFF",
color1: "#EEEEEE",
color2: "#CCC",
}
light: {
backgroundColor0: "#FFFFFF",
backgroundColor1: "#F7F7F7",
backgroundColor2: "#DDDDDD",
color0: "#000000",
color1: "#111111",
color2: "#555",
}
*/

View file

@ -0,0 +1,126 @@
import type { Theme } from "../.."
import { readFileSync } from "fs"
import merge from "merge-deep"
import { DeepPartial } from "utility-types"
import BaseTheme from "../dark"
export default merge<Theme, DeepPartial<Theme>>(BaseTheme, {
color: {
text: {
highContrast: "#000000",
default: "#111111",
gray: "#555",
},
background: "#F7F7F7",
},
component: {
anchor: {
color: {
header: "#D3D3D3",
},
},
blockQuote: {
color: {
background: "#0000000D",
borderLeft: "#0000001A",
},
},
card: {
color: {
background: "#FFFFFF",
hoverGlow: "#00000040",
},
},
code: {
inline: {
color: {
text: "#000000",
background: "#EEE",
border: "#BBB",
},
},
block: {
color: {
border: "#BBB",
highlight: "#DDDDDD",
},
style: readFileSync(__dirname + "/codeblock.css", "utf-8"),
},
},
footer: {
color: {
background: "#FFFFFF",
text: "",
},
},
input: {
color: {
background: {
default: "#EEEEEE",
itemHover: "#FFFFFF",
},
border: {
default: "#CCCCCC",
hover: "#808080",
focus: "#000000",
},
placeHolder: "#777777",
},
},
kbd: {
color: {
text: "#333333",
border: "#CCCCCC",
outerShadow: "#00000033",
innerShadow: "#FFFFFF",
background: "#F7F7F7",
},
},
mark: {
color: {
text: "#000000",
background: "#FFFF00BF",
},
},
scrollbar: {
color: {
track: "#FFFFFF",
thumb: "#DDDDDD",
},
},
scrollProgressBar: {
color: {
background: "#d4d4d8", // zinc-300
foreground: "#52525b", // zinc-600
},
},
table: {
color: {
border: "#DDD",
even: "#F2F2F2",
},
},
ui: {
color: {
background: {
default: "#FFFFFF",
hover: "#EEEEEE",
},
border: "#CCC",
},
},
},
}) as Theme

View file

@ -0,0 +1,9 @@
{
"compilerOptions": {
"types": ["node"],
"moduleResolution": "Node",
"esModuleInterop": true
},
"include": ["src"],
"exclude": ["dist", "node_modules"]
}

View file

@ -1,60 +0,0 @@
interface Theme extends Object {
font: {
sansSerif: string
monospace: string
}
maxDisplayWidth: {
mobile: string
desktop: string
}
component: {
scrollbar: {
width: string
borderRadius: string
}
header: {
color: {
background: string
hover: string
text: string
}
height: string
}
scrollProgressBar: {
color: {
background: string
foreground: string
}
}
card: {
color: {
background: string
}
}
footer: {
color: {
background: string
text: string
}
}
ui: {
color: {
background: {
interactive: string
}
text: {
on1: string
link: string
linkActive: string
}
}
}
}
}

207
pnpm-lock.yaml generated
View file

@ -18,8 +18,9 @@ importers:
apps/blog:
specifiers:
'@developomp-site/eslint-config': workspace:0.0.0
'@developomp-site/tsconfig': workspace:0.0.0
'@developomp-site/eslint-config': workspace:*
'@developomp-site/theme': workspace:*
'@developomp-site/tsconfig': workspace:*
'@fortawesome/fontawesome-svg-core': ^6.2.1
'@fortawesome/free-brands-svg-icons': ^6.2.1
'@fortawesome/free-regular-svg-icons': ^6.2.1
@ -81,6 +82,7 @@ importers:
tslint-config-prettier: ^1.18.0
typescript: ^4.9.3
dependencies:
'@developomp-site/theme': link:../../packages/theme
'@fortawesome/fontawesome-svg-core': 6.2.1
'@fortawesome/free-brands-svg-icons': 6.2.1
'@fortawesome/free-regular-svg-icons': 6.2.1
@ -234,9 +236,23 @@ importers:
packages/theme:
specifiers:
'@types/merge-deep': ^3.0.0
'@types/node': ^18.11.10
merge-deep: ^3.0.3
nodemon: ^2.0.20
tailwindcss: ^3.1.4
ts-node: ^10.9.1
tsup: ^5.10.1
utility-types: ^3.10.0
devDependencies:
tailwindcss: 3.2.4_postcss@8.4.19
'@types/merge-deep': 3.0.0
'@types/node': 18.11.11
merge-deep: 3.0.3
nodemon: 2.0.20
tailwindcss: 3.2.4_v776zzvn44o7tpgzieipaairwm
ts-node: 10.9.1_bspv7bpieoza2i5ctiw2ofswem
tsup: 5.12.9_h63whvv5d2b4avkcl4ixnozygi
utility-types: 3.10.0
packages/tsconfig:
specifiers: {}
@ -4372,6 +4388,10 @@ packages:
resolution: {integrity: sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==}
dev: true
/@types/merge-deep/3.0.0:
resolution: {integrity: sha512-t5B5UfacpaP8opUvFGUwT0uQetFrD+qm1/I2ksxokJFLT0Tb4B2NI2G2LYz3ugMDKOE7adkNBZ6coK7RW6MAqA==}
dev: true
/@types/mime/3.0.1:
resolution: {integrity: sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==}
dev: false
@ -5003,6 +5023,10 @@ packages:
/abab/2.0.6:
resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==}
/abbrev/1.1.1:
resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==}
dev: true
/accepts/1.3.8:
resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
engines: {node: '>= 0.6'}
@ -6451,6 +6475,17 @@ packages:
wrap-ansi: 7.0.0
dev: true
/clone-deep/0.2.4:
resolution: {integrity: sha512-we+NuQo2DHhSl+DP6jlUiAhyAjBQrYnpOk15rN6c6JSPScjiCLh8IbSU+VTcph6YS3o7mASE8a0+gbZ7ChLpgg==}
engines: {node: '>=0.10.0'}
dependencies:
for-own: 0.1.5
is-plain-object: 2.0.4
kind-of: 3.2.2
lazy-cache: 1.0.4
shallow-clone: 0.1.2
dev: true
/clone-deep/4.0.1:
resolution: {integrity: sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==}
engines: {node: '>=6'}
@ -7206,6 +7241,18 @@ packages:
dependencies:
ms: 2.1.3
/debug/3.2.7_supports-color@5.5.0:
resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
peerDependencies:
supports-color: '*'
peerDependenciesMeta:
supports-color:
optional: true
dependencies:
ms: 2.1.3
supports-color: 5.5.0
dev: true
/debug/4.3.4:
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
engines: {node: '>=6.0'}
@ -8953,11 +9000,23 @@ packages:
optional: true
dev: false
/for-in/0.1.8:
resolution: {integrity: sha512-F0to7vbBSHP8E3l6dCjxNOLuSFAACIxFy3UehTUlG7svlXi37HHsDkyVcHo0Pq8QwrE+pXvWSVX3ZT1T9wAZ9g==}
engines: {node: '>=0.10.0'}
dev: true
/for-in/1.0.2:
resolution: {integrity: sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==}
engines: {node: '>=0.10.0'}
dev: true
/for-own/0.1.5:
resolution: {integrity: sha512-SKmowqGTJoPzLO1T0BBJpkfp3EMacCMOuH40hOUbrbzElVktk4DioXVM99QkLCyKoiuOmyjgcWMpVz2xjE7LZw==}
engines: {node: '>=0.10.0'}
dependencies:
for-in: 1.0.2
dev: true
/foreground-child/2.0.0:
resolution: {integrity: sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==}
engines: {node: '>=8.0.0'}
@ -9914,6 +9973,10 @@ packages:
resolution: {integrity: sha512-DUNFN5j7Tln0D+TxzloUjKB+CtVu6myn0JEFak6dG18mNt9YkQ6lzGCdafwofISZ1lLF3xRHJ98VKy9ynkcFaA==}
dev: true
/ignore-by-default/1.0.1:
resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==}
dev: true
/ignore/4.0.6:
resolution: {integrity: sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==}
engines: {node: '>= 4'}
@ -11279,6 +11342,13 @@ packages:
commander: 8.3.0
dev: false
/kind-of/2.0.1:
resolution: {integrity: sha512-0u8i1NZ/mg0b+W3MGGw5I7+6Eib2nx72S/QvXa0hYjEkjTknYmEYQJwGu3mLC0BrhtJjtQafTkyRUQ75Kx0LVg==}
engines: {node: '>=0.10.0'}
dependencies:
is-buffer: 1.1.6
dev: true
/kind-of/3.2.2:
resolution: {integrity: sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==}
engines: {node: '>=0.10.0'}
@ -11320,6 +11390,16 @@ packages:
language-subtag-registry: 0.3.22
dev: false
/lazy-cache/0.2.7:
resolution: {integrity: sha512-gkX52wvU/R8DVMMt78ATVPFMJqfW8FPz1GZ1sVHBVQHmu/WvhIWE4cE1GBzhJNFicDeYhnwp6Rl35BcAIM3YOQ==}
engines: {node: '>=0.10.0'}
dev: true
/lazy-cache/1.0.4:
resolution: {integrity: sha512-RE2g0b5VGZsOCFOCgP7omTRYFqydmZkBwl5oNnQ1lDYC57uyO9KqNnNVxT7COSHTxrRCWVcAVOcbjk+tvh/rgQ==}
engines: {node: '>=0.10.0'}
dev: true
/lazy-cache/2.0.2:
resolution: {integrity: sha512-7vp2Acd2+Kz4XkzxGxaB1FWOi8KjWIWsgdfD5MCb86DWvlLqhRPM+d6Pro3iNEL5VT9mstz5hKAlcd+QR6H3aA==}
engines: {node: '>=0.10.0'}
@ -11776,6 +11856,15 @@ packages:
dev: true
optional: true
/merge-deep/3.0.3:
resolution: {integrity: sha512-qtmzAS6t6grwEkNrunqTBdn0qKwFgNWvlxUbAV8es9M7Ot1EbyApytCnvE0jALPa46ZpKDUo527kKiaWplmlFA==}
engines: {node: '>=0.10.0'}
dependencies:
arr-union: 3.1.0
clone-deep: 0.2.4
kind-of: 3.2.2
dev: true
/merge-descriptors/1.0.1:
resolution: {integrity: sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==}
@ -11982,6 +12071,14 @@ packages:
is-extendable: 1.0.1
dev: true
/mixin-object/2.0.1:
resolution: {integrity: sha512-ALGF1Jt9ouehcaXaHhn6t1yGWRqGaHkPFndtFVHfZXOvkIZ/yoGaSi0AHVTafb3ZBGg4dr/bDwnaEKqCXzchMA==}
engines: {node: '>=0.10.0'}
dependencies:
for-in: 0.1.8
is-extendable: 0.1.1
dev: true
/mkdirp/0.5.6:
resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==}
hasBin: true
@ -12150,6 +12247,30 @@ packages:
/node-releases/2.0.6:
resolution: {integrity: sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==}
/nodemon/2.0.20:
resolution: {integrity: sha512-Km2mWHKKY5GzRg6i1j5OxOHQtuvVsgskLfigG25yTtbyfRGn/GNvIbRyOf1PSCKJ2aT/58TiuUsuOU5UToVViw==}
engines: {node: '>=8.10.0'}
hasBin: true
dependencies:
chokidar: 3.5.3
debug: 3.2.7_supports-color@5.5.0
ignore-by-default: 1.0.1
minimatch: 3.1.2
pstree.remy: 1.1.8
semver: 5.7.1
simple-update-notifier: 1.1.0
supports-color: 5.5.0
touch: 3.1.0
undefsafe: 2.0.5
dev: true
/nopt/1.0.10:
resolution: {integrity: sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==}
hasBin: true
dependencies:
abbrev: 1.1.1
dev: true
/normalize-package-data/2.5.0:
resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==}
dependencies:
@ -13080,7 +13201,6 @@ packages:
postcss: 8.4.19
ts-node: 10.9.1_bspv7bpieoza2i5ctiw2ofswem
yaml: 1.10.2
dev: false
/postcss-loader/4.3.0_gzaxsinx64nntyd3vmdqwl7coe:
resolution: {integrity: sha512-M/dSoIiNDOo8Rk0mUqoj4kpGq91gcxCfb9PoyZVdZ76/AuhxylHDYZblNE8o+EQ9AMSASeMFEKxZf5aU6wlx1Q==}
@ -13748,6 +13868,10 @@ packages:
/psl/1.9.0:
resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==}
/pstree.remy/1.1.8:
resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==}
dev: true
/public-encrypt/4.0.3:
resolution: {integrity: sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==}
dependencies:
@ -14944,6 +15068,11 @@ packages:
resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==}
hasBin: true
/semver/7.0.0:
resolution: {integrity: sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==}
hasBin: true
dev: true
/semver/7.3.8:
resolution: {integrity: sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==}
engines: {node: '>=10'}
@ -15094,6 +15223,16 @@ packages:
safe-buffer: 5.2.1
dev: true
/shallow-clone/0.1.2:
resolution: {integrity: sha512-J1zdXCky5GmNnuauESROVu31MQSnLoYvlyEn6j2Ztk6Q5EHFIhxkMhYcv6vuDzl2XEzoRr856QwzMgWM/TmZgw==}
engines: {node: '>=0.10.0'}
dependencies:
is-extendable: 0.1.1
kind-of: 2.0.1
lazy-cache: 0.2.7
mixin-object: 2.0.1
dev: true
/shallow-clone/3.0.1:
resolution: {integrity: sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==}
engines: {node: '>=8'}
@ -15149,6 +15288,13 @@ packages:
engines: {node: '>=0.12.18'}
dev: true
/simple-update-notifier/1.1.0:
resolution: {integrity: sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==}
engines: {node: '>=8.10.0'}
dependencies:
semver: 7.0.0
dev: true
/sisteransi/1.0.5:
resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==}
@ -15858,7 +16004,6 @@ packages:
resolve: 1.22.1
transitivePeerDependencies:
- ts-node
dev: false
/tapable/1.1.3:
resolution: {integrity: sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==}
@ -16105,6 +16250,13 @@ packages:
resolution: {integrity: sha512-gVweAectJU3ebq//Ferr2JUY4WKSDe5N+z0FvjDncLGyHmIDoxgY/2Ie4qfEIDm4IS7OA6Rmdm7pdEEdMcV/xQ==}
dev: true
/touch/3.1.0:
resolution: {integrity: sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==}
hasBin: true
dependencies:
nopt: 1.0.10
dev: true
/tough-cookie/4.1.2:
resolution: {integrity: sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==}
engines: {node: '>=6'}
@ -16237,6 +16389,42 @@ packages:
hasBin: true
dev: true
/tsup/5.12.9_h63whvv5d2b4avkcl4ixnozygi:
resolution: {integrity: sha512-dUpuouWZYe40lLufo64qEhDpIDsWhRbr2expv5dHEMjwqeKJS2aXA/FPqs1dxO4T6mBojo7rvo3jP9NNzaKyDg==}
hasBin: true
peerDependencies:
'@swc/core': ^1
postcss: ^8.4.12
typescript: ^4.1.0
peerDependenciesMeta:
'@swc/core':
optional: true
postcss:
optional: true
typescript:
optional: true
dependencies:
bundle-require: 3.1.2_esbuild@0.14.54
cac: 6.7.14
chokidar: 3.5.3
debug: 4.3.4
esbuild: 0.14.54
execa: 5.1.1
globby: 11.1.0
joycon: 3.1.1
postcss: 8.4.19
postcss-load-config: 3.1.4_v776zzvn44o7tpgzieipaairwm
resolve-from: 5.0.0
rollup: 2.79.1
source-map: 0.8.0-beta.0
sucrase: 3.29.0
tree-kill: 1.2.2
typescript: 4.9.4
transitivePeerDependencies:
- supports-color
- ts-node
dev: true
/tsup/5.12.9_o6pf6py3ttho7dpy7mshkpxque:
resolution: {integrity: sha512-dUpuouWZYe40lLufo64qEhDpIDsWhRbr2expv5dHEMjwqeKJS2aXA/FPqs1dxO4T6mBojo7rvo3jP9NNzaKyDg==}
hasBin: true
@ -16469,6 +16657,10 @@ packages:
has-symbols: 1.0.3
which-boxed-primitive: 1.0.2
/undefsafe/2.0.5:
resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==}
dev: true
/unfetch/4.2.0:
resolution: {integrity: sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==}
dev: true
@ -16733,6 +16925,11 @@ packages:
/utila/0.4.0:
resolution: {integrity: sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==}
/utility-types/3.10.0:
resolution: {integrity: sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==}
engines: {node: '>= 4'}
dev: true
/utils-merge/1.0.1:
resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==}
engines: {node: '>= 0.4.0'}