This commit is contained in:
Morten Olsen
2023-03-28 08:10:46 +02:00
parent 9b1a067d56
commit 7adf03c83f
44 changed files with 1780 additions and 411 deletions

31
content/config.ts Normal file
View File

@@ -0,0 +1,31 @@
import { Config } from '../types/config';
const config: Config = {
profile: {
path: 'profile.yml',
},
frontpage: {
react: {
template: 'templates/react/pages/frontpage/index.tsx',
},
},
resume: {
latex: {
template: 'templates/latex/resume.tex',
},
},
articles: {
pattern: 'articles/**/*.md',
react: {
template: 'templates/react/pages/article/index.tsx',
},
latex: {
template: 'templates/latex/article.tex',
},
},
positions: {
pattern: 'resume/positions/**/*.md',
},
};
export default config;

View File

@@ -5,4 +5,4 @@ from: 2022
to: Present
---
Hello world
// TODO

View File

@@ -1,9 +1,28 @@
\documentclass{article}
\usepackage[top=2cm, bottom=2cm, left=2cm, right=2cm]{geometry}
\usepackage{graphicx}
\usepackage{hyperref}
\usepackage{multicol}
\usepackage{fancyhdr}
\pagestyle{fancy}
\fancyhf{}
\rhead{<%-profile.name%> \today}
\lhead{<%-article.title%>}
\rfoot{Page \thepage}
\title{<%-article.title%>}
\begin{document}
\maketitle
\includegraphics[width=0.5\textwidth]{<%-article.cover%>}
\begin{multicols}{2}
\noindent\begin{minipage}{\linewidth}
\Huge{<%-article.title%>}
\newline
\large{By <%-profile.name%>}
\vspace{0.5cm}\\
\includegraphics[width=\linewidth]{<%-article.cover%>}
\vspace{1.5cm}
\end{minipage}
<%-article.body%>
\end{multicols}
\end{document}

View File

@@ -1,9 +1,9 @@
import React, { useMemo } from "react";
import styled from "styled-components";
import ArticlePreview from "../preview";
import { JumboArticlePreview } from "../preview/jumbo";
import { MiniArticlePreview } from "../preview/mini";
import { Article } from "types";
import React, { useMemo } from 'react';
import styled from 'styled-components';
import ArticlePreview from '../preview';
import { JumboArticlePreview } from '../preview/jumbo';
import { MiniArticlePreview } from '../preview/mini';
import { Article } from 'types';
type Props = {
articles: Article[];
@@ -47,7 +47,7 @@ const ArticleGrid: React.FC<Props> = ({ articles }) => {
// new Date(b.published).getTime() -
// new Date(a.published).getTime()
// ),
[articles]
[articles],
);
const featured1 = useMemo(() => sorted.slice(0, 1)[0], [sorted]);

View File

@@ -1,9 +1,9 @@
import React, { useMemo } from "react";
import styled from "styled-components";
import { Title1 } from "@/typography";
import { createTheme } from "@/theme/create";
import { ThemeProvider } from "@/theme/provider";
import { Article } from "types";
import React, { useMemo } from 'react';
import styled from 'styled-components';
import { Title1 } from '@/typography';
import { createTheme } from '@/theme/create';
import { ThemeProvider } from '@/theme/provider';
import { Article } from 'types';
type Props = {
article: Article;
@@ -28,7 +28,7 @@ const Wrapper = styled.a`
const Title = styled(Title1)`
background: ${({ theme }) => theme.colors.primary};
line-height: 40px;
font-family: "Black Ops One", sans-serif;
font-family: 'Black Ops One', sans-serif;
font-size: 25px;
padding: 0 5px;
margin: 5px 0;
@@ -48,7 +48,7 @@ const AsideWrapper = styled.aside<{
background: ${({ theme }) => theme.colors.primary};
background-size: cover;
background-position: center;
${({ image }) => (image ? `background-image: url(${image});` : "")}
${({ image }) => (image ? `background-image: url(${image});` : '')}
flex: 1;
top: 0;
bottom: 0;
@@ -63,14 +63,14 @@ const ArticlePreview: React.FC<Props> = ({ article }) => {
createTheme({
baseColor: article.color,
}),
[article.color]
[article.color],
);
return (
<ThemeProvider theme={theme}>
<Wrapper href={`/articles/${article.slug}`}>
<AsideWrapper image={article.thumbUrl} />
<MetaWrapper>
{article.title.split(" ").map((word, index) => (
{article.title.split(' ').map((word, index) => (
<Title key={index}>{word}</Title>
))}
</MetaWrapper>

View File

@@ -1,7 +1,7 @@
import React from "react";
import styled from "styled-components";
import { Title1, Body1 } from "@/typography";
import { Article } from "types";
import React from 'react';
import styled from 'styled-components';
import { Title1, Body1 } from '@/typography';
import { Article } from 'types';
type Props = {
article: Article;
@@ -24,7 +24,7 @@ const Wrapper = styled.a`
const Title = styled(Title1)`
line-height: 40px;
font-family: "Black Ops One", sans-serif;
font-family: 'Black Ops One', sans-serif;
font-size: 25px;
padding: 0 5px;
margin: 5px 0;
@@ -55,7 +55,7 @@ const AsideWrapper = styled.aside<{
background: ${({ theme }) => theme.colors.primary};
background-size: cover;
background-position: center;
${({ image }) => (image ? `background-image: url(${image});` : "")}
${({ image }) => (image ? `background-image: url(${image});` : '')}
flex: 1;
top: 0;
bottom: 0;

View File

@@ -1,9 +1,9 @@
import React, { useMemo } from "react";
import styled from "styled-components";
import { Title1 } from "@/typography";
import { createTheme } from "@/theme/create";
import { ThemeProvider } from "@/theme/provider";
import { Article } from "types";
import React, { useMemo } from 'react';
import styled from 'styled-components';
import { Title1 } from '@/typography';
import { createTheme } from '@/theme/create';
import { ThemeProvider } from '@/theme/provider';
import { Article } from 'types';
type Props = {
article: Article;
@@ -26,7 +26,7 @@ const Title = styled(Title1)`
line-height: 20px;
font-size: 20px;
padding: 5px 5px;
font-family: "Black Ops One", sans-serif;
font-family: 'Black Ops One', sans-serif;
margin: 5px 0;
background: ${({ theme }) => theme.colors.background};
`;
@@ -46,7 +46,7 @@ const AsideWrapper = styled.aside<{
background: ${({ theme }) => theme.colors.primary};
background-size: cover;
background-position: center;
${({ image }) => (image ? `background-image: url(${image});` : "")}
${({ image }) => (image ? `background-image: url(${image});` : '')}
position: absolute;
top: 0;
bottom: 0;
@@ -61,14 +61,14 @@ const MiniArticlePreview: React.FC<Props> = ({ article }) => {
createTheme({
baseColor: article.color,
}),
[article.color]
[article.color],
);
return (
<ThemeProvider theme={theme}>
<Wrapper href={`/articles/${article.slug}`}>
<AsideWrapper image={article.thumbUrl} />
<MetaWrapper>
{article.title.split(" ").map((word, index) => (
{article.title.split(' ').map((word, index) => (
<Title key={index}>{word}</Title>
))}
</MetaWrapper>

View File

@@ -1,4 +1,4 @@
import { FC, ReactNode } from "react"
import { FC, ReactNode } from 'react';
type HtmlProps = {
body: ReactNode;
@@ -17,8 +17,11 @@ const Html: FC<HtmlProps> = ({ body, head, scripts }) => {
))}
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="anonymous"/>
<link href="https://fonts.googleapis.com/css2?family=Archivo+Black&family=Black+Ops+One&family=Merriweather:wght@400;700&display=swap" rel="stylesheet" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="anonymous" />
<link
href="https://fonts.googleapis.com/css2?family=Archivo+Black&family=Black+Ops+One&family=Merriweather:wght@400;700&display=swap"
rel="stylesheet"
/>
</head>
<body>
<div id="root">{body}</div>

View File

@@ -1,7 +1,7 @@
import React, { ReactNode, useMemo } from "react";
import styled from "styled-components";
import { createTheme } from "@/theme/create";
import { ThemeProvider } from "@/theme/provider";
import React, { ReactNode, useMemo } from 'react';
import styled from 'styled-components';
import { createTheme } from '@/theme/create';
import { ThemeProvider } from '@/theme/provider';
const Wrapper = styled.div`
background: ${({ theme }) => theme.colors.background};
@@ -25,7 +25,7 @@ const BackgroundWrapper = styled.div<{
background-size: cover;
background-position: center;
opacity: 0.2;
${({ image }) => (image ? `background-image: url(${image});` : "")}
${({ image }) => (image ? `background-image: url(${image});` : '')}
`;
const Content = styled.div`
@@ -50,7 +50,7 @@ const Sheet: React.FC<Props> = ({ color, background, children }) => {
createTheme({
baseColor: color,
}),
[color]
[color],
);
return (
<ThemeProvider theme={theme}>

View File

@@ -1,9 +1,9 @@
import styled, { createGlobalStyle } from "styled-components";
import ReactMarkdown from "react-markdown";
import { Jumbo } from "./typography";
import { createTheme, ThemeProvider } from "./theme";
import { Helmet } from "react-helmet-async";
import { Page } from "types";
import styled, { createGlobalStyle } from 'styled-components';
import ReactMarkdown from 'react-markdown';
import { Jumbo } from '../../typography';
import { createTheme, ThemeProvider } from '../../theme';
import { Helmet } from 'react-helmet-async';
import { Page } from 'types';
const GlobalStyle = createGlobalStyle`
* { box-sizing: border-box; }
@@ -36,7 +36,7 @@ const ArticleTitleWord = styled(Jumbo)`
padding: 0 15px;
text-transform: uppercase;
margin: 10px;
font-family: "Black Ops One", sans-serif;
font-family: 'Black Ops One', sans-serif;
background: ${({ theme }) => theme.colors.primary};
color: ${({ theme }) => theme.colors.foreground};
@media only screen and (max-width: 900px) {
@@ -57,7 +57,7 @@ const Wrapper = styled.div`
const ArticleWrapper = styled.article`
font-size: 1.1rem;
font-family: "Merriweather", serif;
font-family: 'Merriweather', serif;
> p,
ul,
@@ -82,7 +82,7 @@ const ArticleWrapper = styled.article`
}
> p:first-of-type::first-letter {
font-family: "Black Ops One", sans-serif;
font-family: 'Black Ops One', sans-serif;
border: solid 5px ${({ theme }) => theme.colors.foreground};
margin: 0 1rem 0 0;
font-size: 6rem;
@@ -118,7 +118,7 @@ const ArticleWrapper = styled.article`
padding-right: 40px;
shape-outside: padding-box;
position: relative;
font-family: "Black Ops One", sans-serif;
font-family: 'Black Ops One', sans-serif;
text-transform: uppercase;
display: flex;
align-items: flex-start;
@@ -132,7 +132,7 @@ const ArticleWrapper = styled.article`
&:after {
position: absolute;
content: "";
content: '';
right: 20px;
top: 0;
bottom: 0;
@@ -162,14 +162,14 @@ const ArticleWrapper = styled.article`
&:before {
color: ${({ theme }) => theme.colors.primary};
content: "\\00BB";
content: '\\00BB';
float: left;
font-size: 6rem;
}
&:after {
position: absolute;
content: "";
content: '';
right: 20px;
top: 0;
bottom: 0;
@@ -222,14 +222,14 @@ const Download = styled.a`
text-align: center;
padding: 1rem;
font-size: 1rem;
font-family: "Black Ops One", sans-serif;
font-family: 'Black Ops One', sans-serif;
text-transform: uppercase;
text-decoration: none;
`;
const Author = styled.a`
text-transform: uppercase;
font-family: "Black Ops One", sans-serif;
font-family: 'Black Ops One', sans-serif;
font-size: 2rem;
margin: 1rem;
display: inline-block;
@@ -238,7 +238,7 @@ const Author = styled.a`
color: ${({ theme }) => theme.colors.foreground};
&:after {
content: "";
content: '';
border-bottom: solid 15px ${({ theme }) => theme.colors.primary};
display: block;
width: 100%;
@@ -249,17 +249,13 @@ const Author = styled.a`
}
`;
const ArticlePage: Page<"article"> = ({ article, profile, pdfUrl }) => {
const ArticlePage: Page<'article'> = ({ article, profile, pdfUrl }) => {
return (
<ThemeProvider theme={createTheme({ baseColor: article.color })}>
<Helmet>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link
rel="preconnect"
href="https://fonts.gstatic.com"
crossOrigin="anonymous"
/>
<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="anonymous" />
<link
href="https://fonts.googleapis.com/css2?family=Archivo+Black&family=Black+Ops+One&family=Merriweather:wght@400;700&display=swap"
rel="stylesheet"
@@ -269,7 +265,7 @@ const ArticlePage: Page<"article"> = ({ article, profile, pdfUrl }) => {
<Wrapper>
<Content>
<Title>
{article.title.split(" ").map((word, index) => (
{article.title.split(' ').map((word, index) => (
<ArticleTitleWord key={index}>{word}</ArticleTitleWord>
))}
<Author href="/">by {profile.name}</Author>

View File

@@ -1,12 +1,12 @@
import styled, { createGlobalStyle } from "styled-components";
import { ArticleGrid } from "@/components/article/grid";
import { Jumbo } from "@/typography";
import { useMemo } from "react";
import { Sheet } from "./components/sheet";
import { ThemeProvider, createTheme } from "./theme";
import chroma from "chroma-js";
import { Helmet } from "react-helmet-async";
import { Page } from "../../../types";
import styled, { createGlobalStyle } from 'styled-components';
import { ArticleGrid } from '@/components/article/grid';
import { Jumbo } from '@/typography';
import { useMemo } from 'react';
import { Sheet } from '../../components/sheet';
import { ThemeProvider, createTheme } from '@/theme';
import chroma from 'chroma-js';
import { Helmet } from 'react-helmet-async';
import { Page } from 'types';
const GlobalStyle = createGlobalStyle`
* { box-sizing: border-box; }
@@ -32,7 +32,7 @@ const Download = styled.a`
padding: 0 15px;
text-transform: uppercase;
margin: 10px;
font-family: "Black Ops One", sans-serif;
font-family: 'Black Ops One', sans-serif;
@media only screen and (max-width: 700px) {
margin: 5px;
font-size: 3rem;
@@ -49,7 +49,7 @@ const Title = styled(Jumbo)`
padding: 0 15px;
text-transform: uppercase;
margin: 10px;
font-family: "Black Ops One", sans-serif;
font-family: 'Black Ops One', sans-serif;
@media only screen and (max-width: 700px) {
margin: 5px;
font-size: 3rem;
@@ -71,7 +71,7 @@ const Arrow = styled.div`
border-radius: 50%;
width: 80px;
height: 80px;
content: "↓";
content: '↓';
font-size: 50px;
@media only screen and (max-width: 700px) {
width: 40px;
@@ -99,13 +99,13 @@ const ImageBg = styled.picture`
}
`;
const FrontPage: Page<"frontpage"> = ({ articles, profile }) => {
const FrontPage: Page<'frontpage'> = ({ articles, profile }) => {
const theme = useMemo(
() =>
createTheme({
baseColor: chroma.random().brighten(1).hex(),
}),
[]
[],
);
return (
@@ -113,11 +113,7 @@ const FrontPage: Page<"frontpage"> = ({ articles, profile }) => {
<Helmet>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link
rel="preconnect"
href="https://fonts.gstatic.com"
crossOrigin="anonymous"
/>
<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="anonymous" />
<link
href="https://fonts.googleapis.com/css2?family=Archivo+Black&family=Black+Ops+One&family=Merriweather:wght@400;700&display=swap"
rel="stylesheet"
@@ -130,12 +126,12 @@ const FrontPage: Page<"frontpage"> = ({ articles, profile }) => {
</ImageBg>
<Arrow />
<Hero>
{"Hi, I'm Morten".split(" ").map((char, index) => (
{"Hi, I'm Morten".split(' ').map((char, index) => (
<Title key={index}>{char}</Title>
))}
</Hero>
<Hero>
{"And I make software".split(" ").map((char, index) => (
{'And I make software'.split(' ').map((char, index) => (
<Title key={index}>{char}</Title>
))}
</Hero>
@@ -147,7 +143,7 @@ const FrontPage: Page<"frontpage"> = ({ articles, profile }) => {
</Sheet>
<Sheet color="#ef23e2">
<Hero>
{"Table of Content".split(" ").map((char, index) => (
{'Table of Content'.split(' ').map((char, index) => (
<Title key={index}>{char}</Title>
))}
</Hero>

View File

@@ -11,13 +11,9 @@ type CreateOptions = {
const isBright = (color: chroma.Color) => color.luminance() > 0.4;
const createTheme = (options: CreateOptions = {}) => {
const baseColor = options.baseColor
? chroma(options.baseColor)
: chroma.random();
const baseColor = options.baseColor ? chroma(options.baseColor) : chroma.random();
const text = isBright(baseColor) ? BLACK : WHITE;
const bg = isBright(baseColor)
? baseColor.luminance(0.9)
: baseColor.luminance(0.01);
const bg = isBright(baseColor) ? baseColor.luminance(0.9) : baseColor.luminance(0.01);
const theme: Theme = {
typography: {
Jumbo: {
@@ -57,4 +53,3 @@ const createTheme = (options: CreateOptions = {}) => {
};
export { createTheme };

View File

@@ -3,4 +3,3 @@ import { Theme } from './theme';
declare module 'styled-components' {
export interface DefaultTheme extends Theme {}
}

View File

@@ -1,49 +1,43 @@
import styled from "styled-components";
import { Theme, Typography } from "../theme";
import styled from 'styled-components';
import { Theme, Typography } from '../theme';
interface TextProps {
color?: keyof Theme["colors"];
color?: keyof Theme['colors'];
bold?: boolean;
theme: Theme;
}
const BaseText = styled.span<TextProps>`
${({ theme }) =>
theme.font.family ? `font-family: ${theme.font.family};` : ""}
color: ${({ color, theme }) =>
color ? theme.colors[color] : theme.colors.foreground};
font-weight: ${({ bold }) => (bold ? "bold" : "normal")};
${({ theme }) => (theme.font.family ? `font-family: ${theme.font.family};` : '')}
color: ${({ color, theme }) => (color ? theme.colors[color] : theme.colors.foreground)};
font-weight: ${({ bold }) => (bold ? 'bold' : 'normal')};
font-size: ${({ theme }) => theme.font.baseSize}px;
`;
const get = (name: keyof Theme["typography"], theme: Theme): Typography => {
const get = (name: keyof Theme['typography'], theme: Theme): Typography => {
const typography = theme.typography[name];
return typography;
};
const createTypography = (name: keyof Theme["typography"]) => {
const Component = styled(BaseText) <TextProps>`
font-size: ${({ theme }) =>
theme.font.baseSize * (get(name, theme).size || 1)}px;
const createTypography = (name: keyof Theme['typography']) => {
const Component = styled(BaseText)<TextProps>`
font-size: ${({ theme }) => theme.font.baseSize * (get(name, theme).size || 1)}px;
font-weight: ${({ bold, theme }) =>
typeof bold !== "undefined"
? "bold"
: get(name, theme).weight || "normal"};
${({ theme }) =>
get(name, theme).upperCase ? "text-transform: uppercase;" : ""}
typeof bold !== 'undefined' ? 'bold' : get(name, theme).weight || 'normal'};
${({ theme }) => (get(name, theme).upperCase ? 'text-transform: uppercase;' : '')}
`;
return Component;
};
const Jumbo = createTypography("Jumbo");
const Title2 = createTypography("Title2");
const Title1 = createTypography("Title1");
const Body1 = createTypography("Body1");
const Overline = createTypography("Overline");
const Caption = createTypography("Caption");
const Link = createTypography("Link");
const Jumbo = createTypography('Jumbo');
const Title2 = createTypography('Title2');
const Title1 = createTypography('Title1');
const Body1 = createTypography('Body1');
const Overline = createTypography('Overline');
const Caption = createTypography('Caption');
const Link = createTypography('Link');
const types: { [key in keyof Theme["typography"]]: typeof BaseText } = {
const types: { [key in keyof Theme['typography']]: typeof BaseText } = {
Jumbo,
Title2,
Title1,