feat: added initial resume

This commit is contained in:
Morten Olsen
2024-01-17 20:23:45 +01:00
parent 5f79c8da43
commit 592032240f
33 changed files with 1439 additions and 34 deletions

View File

@@ -1,15 +1,19 @@
import { defineConfig } from 'astro/config';
import mdx from '@astrojs/mdx';
import sitemap from '@astrojs/sitemap';
import tailwind from "@astrojs/tailwind";
import icon from "astro-icon";
// https://astro.build/config
export default defineConfig({
site: 'https://mortenolsen.pro',
integrations: [mdx(), sitemap()],
vite: {
build: {
assetsInlineLimit: 1024 * 10,
}
},
site: 'https://mortenolsen.pro',
integrations: [mdx(), sitemap(), tailwind({
nesting: true
}), icon()],
vite: {
build: {
assetsInlineLimit: 1024 * 10
}
}
});

View File

@@ -14,9 +14,14 @@
"@astrojs/mdx": "^1.1.3",
"@astrojs/rss": "^3.0.0",
"@astrojs/sitemap": "^3.0.2",
"@astrojs/tailwind": "^5.1.0",
"@iconify-json/bxl": "^1.1.10",
"@iconify-json/devicon": "^1.1.35",
"astro": "^3.4.0",
"astro-icon": "^1.0.2",
"chroma-js": "^2.4.2",
"reset-css": "^5.0.2",
"tailwindcss": "^3.0.24",
"typescript": "^5.2.2"
},
"devDependencies": {

741
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,27 @@
---
import type { CollectionEntry } from 'astro:content';
type Props = {
content: CollectionEntry<'references'>;
}
const { content } = Astro.props;
const Body = await content.render();
---
<div class="item">
<div class="flex items-center mb-4">
<div class="flex-1">
<h1 class="text-xl font-bold">{ content.data.name}</h1>
<p class="text-gray-500 text-lg">{ content.data.position} @ {content.data.company}</p>
</div>
</div>
<div class="leading-loose">
<Body.Content />
</div>
<div class="text-gray-500 text-sm mt-4">{content.data.relation}</div>
</div>
<style>
.item {
}
</style>

View File

@@ -0,0 +1,30 @@
---
import { type CollectionEntry } from "astro:content";
import { Image } from "astro:assets";
type Props = {
work: CollectionEntry<"work">;
};
const { work } = Astro.props;
---
<div class="border-slate-300 border-solid border rounded p-4">
<div class="flex items-center gap-2">
{
work.data.logo ? (
<Image
src={work.data.logo}
width={48}
height={48}
class="w-12 h-12 rounded-full object-cover"
alt={work.data.name}
/>
) : null
}
<div class="flex-1">
<h3 class="font-bold text-lg">{work.data.name}</h3>
<h4 class="font-bold">{work.data.position}</h4>
</div>
</div>
{work.data.summary && <div class="text-sm mt-2">{work.data.summary}</div>}
</div>

View File

@@ -0,0 +1,74 @@
---
import { type CollectionEntry } from "astro:content";
import Item from "./Timeline.Item.astro";
type Props = {
work: CollectionEntry<"work">[];
selected: number;
};
const { work, selected } = Astro.props;
const formatDate = (d?: Date) => {
if (!d) {
return "Present";
}
return `${d.toLocaleString("default", {
month: "short",
})} ${d.getFullYear()}`;
};
---
<div class="timeline" style={{ "--length": work.length }}>
<div class="indicator" style={{ "--index": selected }}></div>
<div class="current" style={{ "--index": selected }}></div>
{
work.map((work, index) => (
<>
<div class="time flex justify-center flex-col-reverse text-right text-sm gap-2 text-slate-600">
<div>{formatDate(work.data.startDate)}</div>
<div>{formatDate(work.data.endDate)}</div>
</div>
<div class="item">
<Item work={work} />
</div>
</>
))
}
</div>
<style>
.timeline {
display: grid;
grid-template-columns: auto 60px auto;
grid-auto-flow: columns;
grid-auto-rows: auto;
row-gap: 1rem;
.indicator {
grid-column: 2;
grid-row: 1 / span var(--length);
width: calc(50% + 1px);
@apply border-r border-dashed border-slate-600;
}
.current {
grid-column: 2;
grid-row: 1 / span var(--index);
@apply flex items-center justify-center;
&:after {
content: "";
@apply border border-solid bg-white border-slate-600 rounded-full absolute w-4 h-4;
}
}
.time {
grid-column-start: 1;
}
.item {
break-inside: avoid;
width: fit-content;
grid-column-start: 3;
}
}
</style>

View File

@@ -0,0 +1,34 @@
---
import type { CollectionEntry } from "astro:content";
type Props = {
content: CollectionEntry<"work">;
};
const { content } = Astro.props;
const Body = await content.render();
const formatDate = (d?: Date) => {
if (!d) {
return "Present";
}
return `${d.toLocaleString("default", { month: "long" })} ${d.getFullYear()}`;
};
---
<div class="item">
<div class="md:text-right md:items-end flex flex-col">
<h1 class="text-xl font-bold">{content.data.name}</h1>
<p class="text-gray-500 text-lg">{content.data.position}</p>
<p class="text-gray-500 text-sm">
{formatDate(content.data.startDate)} - {formatDate(content.data.endDate)}
</p>
</div>
<div class="leading-loose">
<Body.Content />
</div>
</div>
<style>
.item {
display: contents;
}
</style>

View File

@@ -14,11 +14,18 @@ const articles = defineCollection({
});
const work = defineCollection({
schema: () => z.object({
schema: ({ image }) => z.object({
name: z.string(),
position: z.string(),
startDate: z.coerce.date(),
endDate: z.coerce.date().optional(),
summary: z.string().optional(),
logo: image().refine((img) => img.width >= 200, {
message: "Logo must be at least 320 pixels wide!",
}).optional(),
banner: image().refine((img) => img.height>= 50, {
message: "Logo must be at least 320 pixels wide!",
}).optional(),
}),
})

View File

@@ -3,7 +3,7 @@ name: Jens Roland
position: Director of Engineering
company: ZeroNorth
date: 2021-10-28
relation: Jens was senior to Morten but didn't manage Morten directlyOctober 28, 2021, Jens was senior to Morten but didn't manage Morten directly
relation: Jens was senior to Morten but didn't manage Morten directly at Trendsales
profile: https://www.linkedin.com/in/jensroland/
---

View File

@@ -3,7 +3,7 @@ name: Ole Højriis Kristensen
position: Software Engineering Manager
company: Apple
date: 2017-11-22
relation: Ole Højriis managed Morten directly
relation: Ole Højriis managed Morten directly at Trendsales
profile: https://www.linkedin.com/in/okristensen/
---

View File

@@ -0,0 +1,10 @@
---
title: Bob the algorithm
link: /articles/bob-the-algorithm
keywords:
- Typescript
- React Native
- Algorithmic
---
`// TODO`

View File

@@ -0,0 +1,9 @@
---
title: Bob the algorithm
link: https://github.com/morten-olsen/mini-loader
keywords:
- Typescript
- Task management
---
`// TODO`

View File

View File

@@ -1,8 +1,8 @@
---
name: Haastrup IT
position: Web developer
startDate: 2009
endDate: 2010
startDate: 2009-03-01
endDate: 2010-05-30
---
I have worked as a part time project koordinator and systems developer, sitting with responsibility for a wide variety of projects including projects for "Københavns Kommune" (Navision reporting software) and "Syddanmarks kommune" (Electronic application processing system). Most projects were made in C#, but also PHP, VB, ActionScript. In addtion to that i maintained the in-house hosting setup.

View File

@@ -1,8 +1,9 @@
---
name: BilZonen
position: Web Developer
startDate: 2010
endDate: 2012
startDate: 2010-06-01
endDate: 2012-02-28
summary: As a part-time web developer at bilzonen.dk, I managed both routine maintenance and major projects like new modules and integrations, introduced a custom provider-model system in .NET (C#) for data management, and established the development environment, including server setup and custom tools for building and testing.
---
I work as a part-time web developer on bilzonen.dk. I have worked with both day-to-day maintenance and large scale projects (new search module, integration of new data catalog, mobile site, new-car-catalog and the entire dealer solution). The page is an Umbraco solution, with all .NET (C#) code. I have introduced a new custom build provider-model system, which allows data-providers to move data between data stores, external services, and the site. (search, caching and external car date is running through the provider system). Also, i have set up the development environment, from setting up virtual server hosts to building custom tool for building and unit testing.

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@@ -1,8 +1,10 @@
---
name: Sampension
position: Senior Frontend Developer
startDate: 2018
endDate: 2021
startDate: 2018-01-01
endDate: 2021-12-31
logo: ./assets/logo.jpeg
summary: At Sampension, a Danish pension fund, I designed and helped build a cross-platform frontend architecture using React Native and React Native for Web, ensuring a unified, maintainable codebase for native iOS, Android, and web applications across devices.
---
Sampension is a danish pension fund and my work has been to design and help to build a frontend architecture that would run natively on iOS and Android as well as on the web on both desktop and mobile devices.

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

@@ -1,8 +1,11 @@
---
name: Trendsales
position: Web Developer
startDate: 2012
endDate: 2012
startDate: 2012-03-01
endDate: 2012-09-30
logo: ./assets/logo.png
banner: ./assets/banner.png
summary: At Trendsales, I started with a part-time role focused on maintaining the API for the iOS app, eventually diversifying my responsibilities to include broader platform development, allocating 25-50% of my time to the API.
---
I got a part-time job at Trendsales, where my primary responsibility was maintaining the API which powered the iOS app. Quickly my tasks became more diverse, and I ended using about 25-50 percent of my time on the API, while the remaining was spend doing work on the platform in general.

View File

@@ -1,8 +1,10 @@
---
name: Trendsales
position: iOS and Android Developer
startDate: 2012
endDate: 2015
startDate: 2012-10-01
endDate: 2015-12-31
logo: ./trendsales-1/assets/logo.png
summary: I led the development of a new Xamarin-based iOS app from scratch at Trendsales, including a supporting API and backend work, culminating in a successful app with over 15 million screen views and 1.5 million sessions per month, and later joined a team to expand into Android development.
---
I became responsible for the iOS platform, which was a task that required a new app to be built from the ground up using _Xamarin_. In addition to that, a new API to support the app along with support for our larger vendors was needed which had to be build using something closely similar to _Microsoft MVC_ so that other people could join the project at a later stage.

View File

@@ -1,8 +1,10 @@
---
name: Trendsales
position: Frontend Technical Lead
startDate: 2016
endDate: 2018
startDate: 2016-01-01
endDate: 2017-12-31
logo: ./trendsales-1/assets/logo.png
summary: In 2015, I spearheaded the creation of a new frontend architecture for Trendsales, leading to the development of m.trendsales.dk, using React and Redux, and devising bespoke frameworks for navigation, flexible routing, skeleton page transitions, and integrating workflows across systems like Github, Jira, Octopus Deploy, AppVeyor, and Docker.
---
In 2015 Trendsales decided to build an entirely new platform. It became my responsibility to create a modernized frontend architecture. The work began in 2016 with just me on the project and consisted of a proof of concept version containing everything from framework selection, structure, style guides build chain, continuous deployment, and an actual initial working version. The result where the platform which I was given technical ownership over and which I, along with two others, worked on expanding over the next year. The platform is currently powering _m.trendsales.dk_. The project is build using React and state management are done using Redux. In addition to the of the shelve frameworks, we also needed to develop quite a few bespoke frameworks, in order to meet demands. Among others, these were created to solve the following issues:

Binary file not shown.

After

Width:  |  Height:  |  Size: 194 KiB

View File

@@ -1,7 +1,9 @@
---
name: ZeroNorth
position: Senior Software Engineer
startDate: 2022
startDate: 2022-01-01
logo: ./assets/logo.png
summary: At Zero North, I develop and maintain a NextJS-based, offline-first PWA for on-vessel reporting, and enhance report processing infrastructure using Terraform and NodeJS.
---
I am currently employed at Zero North, a Danish software as a service company that specializes in providing solutions to help the shipping industry decarbonize through optimization. My primary focus has been on the development and maintenance of the on-vessel reporting platform. This platform is a NextJS based PWA with offline-first capabilities, which allows for easy and efficient reporting on board ships.

View File

@@ -1,4 +1,4 @@
import image from './me.jpg';
import image from './me2.jpg';
const levels = ['Beginner', 'Novice', 'Trained', 'Expert', 'Expert'];
@@ -8,26 +8,31 @@ const languages = [
fluency: 'Conversational'
},
{
name: 'Dansih',
name: 'Danish',
fluency: 'Native speaker'
}
];
const skills = [
{
name: 'Web Development (React)',
name: 'Web Development',
level: levels[5],
keywords: ['TypeScript', 'React', 'Next.js', 'Node.js', 'Fastify']
keywords: ['TypeScript', 'React', 'RTK', 'React Query', 'Vite', 'Webpack', 'Next.js', 'Astro']
},
{
name: 'Mobile development (React Native)',
name: 'Mobile development',
level: levels[4],
keywords: ['TypeScript', 'React Native', 'Expo', 'React Navigation']
keywords: ['TypeScript', 'React Native', 'Expo', 'React Navigation', 'Xamarin']
},
{
name: 'Service development',
level: levels[4],
keywords: ['TypeScript', 'Node.js', 'Fastify', 'tRPC', 'Apollo', 'Knex.js', '.Net', 'Rust']
},
{
name: 'DevOps',
level: levels[3],
keywords: ['Docker', 'Terraform', 'GitHub Actions', 'AWS']
keywords: ['Kubernetes', 'Docker', 'ArgoCD', 'Terraform', 'GitHub Actions', 'AWS']
}
];
@@ -37,7 +42,7 @@ const basics = {
image,
email: 'fbtijfdq@void.black',
url: 'https://mortenolsen.pro',
summary: "Hi, I'm Morten and I make software",
summary: "Hi, I'm Morten and I make software 👋",
location: {
city: 'Copenhagen',
countryCode: 'DK',
@@ -47,11 +52,13 @@ const basics = {
{
network: 'GitHub',
username: 'morten-olsen',
icon: 'devicon:github',
url: 'https://github.com/morten-olsen'
},
{
network: 'LinkedIn',
username: 'mortenolsendk',
icon: 'devicon:linkedin',
url: 'https://www.linkedin.com/in/mortenolsendk/'
}
]

BIN
src/data/profile/me2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

232
src/pages/resume.astro Normal file
View File

@@ -0,0 +1,232 @@
---
import "reset-css";
import "../styles/fonts-resume.css";
import { Image } from "astro:assets";
import { getCollection, type CollectionEntry } from "astro:content";
import Timeline from "../components/resume/Timeline/Timeline.astro";
import { basics, skills, languages } from "../data/profile";
import { Icon } from "astro-icon/components";
const work: CollectionEntry<"work">[] = (await getCollection("work")).sort(
(a, b) => b.data.startDate.getTime() - a.data.startDate.getTime(),
);
---
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Morten Olsen's Resume</title>
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
</head>
<body>
<div class="root">
<div class="basics flex flex-col nsm:flex-row gap-8">
<div
class="flex flex-col nsm:justify-center nsm:items-end nsm:text-right gap-4"
>
<div>
<div class="text-sm font-bold text-gray-600 uppercase">
✉️ Contact
</div>
<div class="text-sm">{basics.email}</div>
</div>
<div>
<div class="text-sm font-bold text-gray-600 uppercase">
🙊 Languages
</div>
<div class="text-sm">
{languages.map((l) => l.name).join(", ")}
</div>
</div>
<div>
<div class="text-sm font-bold text-gray-600 uppercase">
🌐 Location
</div>
<div class="text-sm">
{basics.location.city}, {basics.location.countryCode}
</div>
</div>
<div>
<div class="text-sm font-bold text-gray-600 uppercase">
🕸️ Website
</div>
<div class="text-sm">www.mortenolsen.pro</div>
</div>
</div>
<div class="intro text-justify text-sm">
<Image
src={basics.image}
alt="Morten Olsen"
width={224}
height={224}
class="avatar p-4 mx-auto nsm:float-left w-64 h-64 rounded-full overflow-hidden"
/>
<h1 class="text-4xl font-bold">{basics.name}</h1>
<div class="text-lg text-gray-600 mb-4">{basics.summary}</div>
<div class="leading-relaxed">
<p>
As a software engineer with a diverse skill set in frontend,
backend, and DevOps, I find my greatest satisfaction in unraveling
complex challenges and transforming them into achievable
solutions. My career has predominantly been in frontend
development, but my keen interest and adaptability have frequently
drawn me into backend and DevOps roles. I am driven not by titles
or hierarchy but by opportunities where I can make a real
difference through my work.
</p><p>
In every role, I strive to blend my technical skills with a
collaborative spirit, focusing on contributing to team goals and
delivering practical, effective solutions. My passion for
development extends beyond professional settings; I continually
engage in personal projects to explore new technologies and
methodologies, keeping my skills sharp and current.
</p><p>
I am eager to find a role that aligns with my dedication to
development and problem-solving, a place where I can apply my
varied expertise to meaningful projects and grow within a team
that values innovation and technical skill.
</p>
</div>
<div class="contact flex mt-8 gap-4">
{
basics.profiles.map((profile) => (
<div class="flex items-center gap-2">
<Icon class="text-4xl" name={profile.icon} />
<div>
<div class="text-sm font-bold text-gray-600 uppercase">
{profile.network}
</div>
<a href={profile.url} target="_blank">
{profile.username}
</a>
</div>
</div>
))
}
</div>
</div>
</div>
<div class="skills">
<h2 class="text-2xl font-bold py-4">🐝 Skills (Buzz words)</h2>
<div>
{
skills.map((skill) => (
<div class="mt-4">
<div class="text-sm font-bold text-gray-600 uppercase">
{skill.name}
</div>
<div class="text-sm text-gray-800 mt-2 flex flex-wrap gap-2">
{skill.keywords.map((k) => (
<div class="text-sm rounded flex-shrink-0 border border-slate-300 border-solid px-2 py-1">
{k}
</div>
))}
</div>
</div>
))
}
</div>
</div>
<div class="experience">
<h2 class="text-2xl font-bold py-4">💼 Work experience</h2>
<Timeline work={work} selected={1} />
<a
href="/resume/work-history"
class="text-sm text-gray-600 show-print block mt-2"
>
Read detailed work history
</a>
</div>
</div>
</body>
</html>
<style>
@page {
size: auto; /* auto is the initial value */
margin: 20mm 20mm;
}
.avatar {
shape-outside: circle();
}
body,
html {
print-color-adjust: exact;
font-family: "Leto", sans-serif;
@apply bg-slate-200;
@media print {
font-size: 9pt;
}
}
@media print {
a.show-print {
&:after {
content: " (https://mortenolsen.pro" attr(href) ")";
font-size: 0.8em;
}
}
}
* {
box-sizing: border-box;
}
.root {
width: 100%;
margin: 0 auto;
max-width: 1000px;
@apply bg-slate-100 shadow-lg print:shadow-none p-8 nsm:p-24 gap-8;
@apply print:bg-white print:p-0 print:max-w-none;
display: grid;
grid-template-columns: max-content 1fr;
grid-template-areas:
"basics basics"
"skills skills"
"experience experience";
& > * {
break-inside: avoid;
}
@media only screen and (max-width: 775px) {
grid-template-columns: 1fr;
grid-template-areas:
"intro"
"basics"
"skills"
"experience";
}
.basics {
grid-area: basics;
}
.intro {
grid-area: intro;
}
.contact {
grid-area: contact;
}
.skills {
grid-area: skills;
@apply border-t border-slate-200 border-solid pt-8 mt-8 print:border-none;
}
.experience {
grid-area: experience;
@apply border-t border-slate-200 border-solid pt-8 mt-8 print:border-none;
}
}
</style>

View File

@@ -2,7 +2,7 @@ import { getCollection } from 'astro:content';
import { basics, skills, languages } from '../data/profile';
export async function GET(context: any) {
export async function GET() {
const work = await getCollection('work');
const references = await getCollection('references');
const resume = {

View File

@@ -0,0 +1,79 @@
---
import "reset-css";
import "../../styles/fonts-resume.css";
import { getCollection } from "astro:content";
import Reference from "../../components/resume/References.astro";
const references = await getCollection("references");
---
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Morten Olsen's Resume</title>
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
</head>
<body>
<div class="root">
<h2 class="text-2xl font-bold mb-16">References</h2>
<div class="flex flex-col gap-8">
{references.map((reference) => <Reference content={reference} />)}
</div>
</div>
</body>
</html>
<style>
@page {
size: auto; /* auto is the initial value */
margin: 20mm 20mm;
}
.workgrid {
grid-template-columns: auto 1fr;
grid-gap: 4rem;
}
.page-break {
page-break-after: always;
}
.avatar {
shape-outside: circle();
}
body,
html {
print-color-adjust: exact;
--bg-color: #f5f5f5;
--text-color: #333;
background-color: var(--bg-color);
font-family: "Leto", sans-serif;
@media print {
--bg-color: #fff;
}
}
* {
box-sizing: border-box;
}
.root {
width: 100%;
margin: 0 auto;
max-width: 1000px;
@apply p-8 md:p-16;
@apply print:p-0 print:max-w-none;
}
.withSidebar {
grid-template-columns: min(300px, 50%) 1fr;
column-gap: 50px;
@media print {
display: block;
}
}
</style>

View File

@@ -0,0 +1,82 @@
---
import "reset-css";
import "../../styles/fonts-resume.css";
import { getCollection, type CollectionEntry } from "astro:content";
import Work from "../../components/resume/Work.astro";
const work: CollectionEntry<"work">[] = (await getCollection("work")).sort(
(a, b) => b.data.startDate.getTime() - a.data.startDate.getTime(),
);
---
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Morten Olsen's Resume</title>
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
</head>
<body>
<div class="root">
<h2 class="text-2xl font-bold mb-8 mt-16">Full work history</h2>
<div class="workgrid md:grid flex flex-col">
{work.map((work) => <Work content={work} />)}
</div>
</div>
</body>
</html>
<style>
@page {
size: auto; /* auto is the initial value */
margin: 20mm 20mm;
}
.workgrid {
grid-template-columns: auto 1fr;
grid-gap: 4rem;
}
.page-break {
page-break-after: always;
}
.avatar {
shape-outside: circle();
}
body,
html {
print-color-adjust: exact;
--bg-color: #f5f5f5;
--text-color: #333;
background-color: var(--bg-color);
font-family: "Leto", sans-serif;
@media print {
--bg-color: #fff;
}
}
* {
box-sizing: border-box;
}
.root {
width: 100%;
margin: 0 auto;
max-width: 1000px;
@apply p-8 md:p-16;
@apply print:p-0 print:max-w-none;
}
.withSidebar {
grid-template-columns: min(300px, 50%) 1fr;
column-gap: 50px;
@media print {
display: block;
}
}
</style>

View File

@@ -0,0 +1,36 @@
/* latin-ext */
@font-face {
font-family: 'Lato';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(https://fonts.gstatic.com/s/lato/v24/S6uyw4BMUTPHjxAwXjeu.woff2) format('woff2');
unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Lato';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(https://fonts.gstatic.com/s/lato/v24/S6uyw4BMUTPHjx4wXg.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* latin-ext */
@font-face {
font-family: 'Lato';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url(https://fonts.gstatic.com/s/lato/v24/S6u9w4BMUTPHh6UVSwaPGR_p.woff2) format('woff2');
unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Lato';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url(https://fonts.gstatic.com/s/lato/v24/S6u9w4BMUTPHh6UVSwiPGQ.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

View File

@@ -8,6 +8,9 @@ body {
margin: 0;
font-family: 'Merriweather', serif;
overflow-x: hidden;
media print {
overflow: visible;
}
}
html {

13
tailwind.config.mjs Normal file
View File

@@ -0,0 +1,13 @@
/** @type {import('tailwindcss').Config} */
export default {
content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],
theme: {
extend: {
screens: {
scr: {'raw': 'screen'},
nsm: {'raw': '(min-width: 640px)'},
},
},
},
plugins: [],
}