1
0
Fork 0

wip(fs.dev): Added blog posts

This commit is contained in:
Joost De Cock 2021-12-26 16:43:35 +01:00
parent 6d1e685c1f
commit 7a65152260
34 changed files with 1592 additions and 1120 deletions

93
packages/freesewing.dev/' Normal file
View file

@ -0,0 +1,93 @@
const colors = require('tailwindcss/colors')
const bg = '#002808'
module.exports = {
'fontFamily': `ui-monospace, Menlo, Monaco, "Cascadia Mono", "Segoe UI Mono", "Roboto Mono", "Oxygen Mono", "Ubuntu Monospace", "Source Code Pro", "Fira Mono", "Droid Sans Mono", "Courier New", monospace;`,
'primary': colors.lime['700'],
'primary-focus': colors.lime['600'],
'primary-content': colors.lime['50'],
'secondary': colors.lime['200'],
'secondary-focus': colors.lime['600'],
'secondary-content': bg,
'accent': colors.lime['700'],
'accent-focus': colors.lime['600'],
'accent-content': colors.yellow['200'],
'neutral': colors.lime['700'],
'neutral-focus': colors.lime['600'],
'neutral-content': colors.lime['200'],
'base-100': bg,
'base-200': colors.lime['900'],
'base-300': colors.lime['800'],
'base-content': colors.lime['500'],
'info': colors.lime['700'],
'success': colors.lime['700'],
'warning': colors.lime['700'],
'error': colors.lime['700'],
'--btn-info-content': colors.teal[300],
'--btn-success-content': colors.green[300],
'--btn-warning-content': colors.orange[300],
'--btn-error-content': colors.red[300],
'--rounded-btn': '0',
'--theme-gradient': `repeating-linear-gradient(
-45deg,
${colors.lime['700']},
${colors.lime['700']} 15px,
${bg} 15px,
${bg} 30px
)`,
'--code-background-color': '#002407',
'--code-border-color': colors.lime['900'],
'--code-color': colors.lime['600'],
'--code-font-family': `"SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace`,
'--code-border-radius': 0,
'--code-border-style': 'solid',
'--code-border-width': 1,
'--code-outer-padding': '0 0.5rem',
'--code-inner-padding': '1rem',
'--code-color-keyword': colors.lime['400'],
'--code-font-weight-keyword': 'bold',
'--code-color-entity': colors.lime['400'],
'--code-font-weight-entity': 'bold',
'--code-color-constant': colors.lime['200'],
'--code-color-string': colors.lime['200'],
'--code-font-style-string': 'italic',
'--code-color-variable': colors.lime['400'],
'--code-color-comment': colors.lime['600'],
'--code-color-tag': colors.lime['400'],
'--code-color-property': colors.lime['200'],
'--code-font-weight-property': 'bold',
'--pattern-bg': colors.lime['900'],
'--pattern-fabric': colors.neutral['700'],
'--pattern-lining': colors.emerald['500'],
'--pattern-interfacing': colors.neutral['400'],
'--pattern-canvas': colors.amber['600'],
'--pattern-various': colors.red['500'],
'--pattern-mark': colors.blue['500'],
'--pattern-contrast': colors.pink['500'],
'--pattern-note': colors.violet['500'],
'--pattern-fabric': colors.neutral['700'],
'--pattern-lining': colors.emerald['500'],
'--pattern-interfacing': colors.neutral['400'],
'--pattern-canvas': colors.amber['600'],
'--pattern-various': colors.red['500'],
'--pattern-mark': colors.blue['500'],
'--pattern-contrast': colors.pink['500'],
'--pattern-note': colors.violet['500'],
'--pattern-scale': 1,
'--pattern-stroke-xs': "0.2",
'--pattern-stroke-sm': "0.4",
'--pattern-stroke': "0.7",
'--pattern-stroke-lg': "1.3",
'--pattern-stroke-xl': "2",
'--pattern-stroke-xxl': "4",
}

View file

@ -0,0 +1,54 @@
import NextLink from 'next/link'
const Link = ({ href, txt }) => (
<NextLink href={href}>
<a title={txt} className="hover:underline">{txt}</a>
</NextLink>
)
const social = {
Discord: 'https://discord.freesewing.org/',
Instagram: 'https://instagram.com/freesewing_org',
Facebook: 'https://www.facebook.com/groups/627769821272714/',
Reddit: 'https://www.reddit.com/r/freesewing/',
Twitter: 'https://twitter.com/freesewing_org',
}
const Footer = ({ app }) => {
return (
<footer>
<div className={`theme-gradient h-2 w-full relative ${app.loading ? 'loading' : ''}`}></div>
<div className="p-4 py-16 flex flex-row bg-neutral -mt-2 z-0 gap-8 row-wrap justify-center text-neutral-content">
<div>
<h5 className="text-neutral-content">What is this?</h5>
<div className="theme-gradient h-1 mb-4"></div>
<ul>
<li>
<Link href="https://freesewing.org/docs/guide/what" txt="About FreeSewing" />
</li>
<li>
<Link href="https://freesewing.org/docs/faq" txt="Frequently Asked Questions" />
</li>
<li>
<Link href="https://freesewing.org/patrons/join" txt="Become a Patron" />
</li>
</ul>
</div>
<div>
<h5 className="text-neutral-content">Social Media</h5>
<div className="theme-gradient h-1 mb-4"></div>
<ul>
{Object.keys(social).map(item => <li key={item}><Link href={social[item]} txt={item}/>)}
</ul>
</div>
</div>
</footer>
)
}
export default Footer

View file

@ -1,13 +1,82 @@
import NextLink from 'next/link'
import Logo from 'shared/components/logos/freesewing.js'
const Link = ({ href, txt }) => (
<NextLink href={href}>
<a title={txt} className="hover:underline text-secondary font-bold hover:pointer">{txt}</a>
</NextLink>
)
const link = "text-secondary font-bold hover:pointer hover:underline"
const social = {
Discord: 'https://discord.freesewing.org/',
Instagram: 'https://instagram.com/freesewing_org',
Facebook: 'https://www.facebook.com/groups/627769821272714/',
Github: 'https://github.com/freesewing',
Reddit: 'https://www.reddit.com/r/freesewing/',
Twitter: 'https://twitter.com/freesewing_org',
}
const Footer = ({ app }) => { const Footer = ({ app }) => {
return ( return (
<footer> <footer>
<div className="theme-gradient h-8 w-full relative"></div> <div className={`theme-gradient h-2 w-full relative ${app.loading ? 'loading' : ''}`}></div>
<div className="p-4 flex flex-row bg-neutral -mt-4 z-0"> <div className="p-4 py-16 flex flex-row bg-neutral -mt-2 z-0 gap-8 flex-wrap justify-around text-neutral-content">
<p>Some content here</p> <div className="w-full sm:w-auto">
<p>Some more content here</p> <h5 className="text-neutral-content">What is this?</h5>
<div className="theme-gradient h-1 mb-4"></div>
<ul>
<li>
<Link href="https://freesewing.org/docs/guide/what" txt="About FreeSewing" />
</li>
<li>
<Link href="https://freesewing.org/docs/faq" txt="Frequently Asked Questions" />
</li>
<li>
<Link href="https://freesewing.org/patrons/join" txt="Become a Patron" />
</li>
</ul>
</div>
<div className="w-full sm:w-auto sm:max-w-xs">
<h5 className="text-neutral-content">Where can I turn for help?</h5>
<div className="theme-gradient h-1 mb-2"></div>
<p className="text-sm text-neutral-content">
<a className={link} href={social.discord}>Our Discord server</a> is
the best place to ask questions and get help. It's where our community hangs out
so you'll get the fastest response and might even make a few new friends along the way.
</p>
<p className="text-sm text-neutral-content">
You can also <a href={social.twitter} className={link} >reach out on Twitter</a> or <a
href="https://github.com/freesewing/freesewing/issues/new/choose"
className={link}
> create an issue on Github </a> if Discord is not your jam.
</p>
</div>
<div className="w-full sm:w-auto">
<h5 className="text-neutral-content">Social Media</h5>
<div className="theme-gradient h-1 mb-4"></div>
<ul>
{Object.keys(social).map(item => <li key={item}><Link href={social[item]} txt={item}/></li>)}
</ul>
</div>
<div className="text-center">
<Logo fill='currentColor' stroke='none' size={164} className="m-auto"/>
<h5 className="text-neutral-content">FreeSewing</h5>
<p className="bold text-neutral-content text-sm">
Come for the sewing patterns
<br />
Stay for the community
</p>
</div>
</div> </div>
</footer> </footer>
) )
} }
export default Footer export default Footer

View file

@ -1,3 +1,6 @@
import Logo from 'shared/components/logos/freesewing.js'
import Link from 'next/link'
const Right = props => ( const Right = props => (
<svg xmlns="http://www.w3.org/2000/svg" className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <svg xmlns="http://www.w3.org/2000/svg" className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 8l4 4m0 0l-4 4m4-4H3" /> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 8l4 4m0 0l-4 4m4-4H3" />
@ -17,7 +20,7 @@ const Header = ({ app }) => {
sm:hidden sm:hidden
z-30 z-30
`}> `}>
<div className="p-2"> <div className="p-2 flex flex-row gap-2 justify-between">
<button <button
className={` className={`
btn border-base-100 text-base-100 btn-sm border border-transparent bg-transparent btn border-base-100 text-base-100 btn-sm border border-transparent bg-transparent
@ -29,8 +32,14 @@ const Header = ({ app }) => {
: <>Show menu &nbsp;<Right /></> : <>Show menu &nbsp;<Right /></>
} }
</button> </button>
<Logo size={32} color="#fff"/>
<Link href="/">
<button className="btn btn-link btn-sm">
freesewing.dev
</button>
</Link>
</div> </div>
<div className="theme-gradient h-2 w-full z-10 relative -mb-2"></div> <div className={`theme-gradient h-2 w-full z-10 relative -mb-2 ${app.loading ? 'loading' : ''}`}></div>
</header> </header>
) )
} }

View file

@ -23,7 +23,9 @@
"lodash.orderby": "^4.6.0", "lodash.orderby": "^4.6.0",
"lodash.set": "^4.3.2", "lodash.set": "^4.3.2",
"next": "latest", "next": "latest",
"react-markdown": "^7.1.1",
"react-swipeable": "^6.2.0", "react-swipeable": "^6.2.0",
"react-timeago": "^6.2.1",
"rehype-highlight": "^5.0.1", "rehype-highlight": "^5.0.1",
"remark-copy-linked-files": "^1.5.0", "remark-copy-linked-files": "^1.5.0",
"remark-gfm": "^3.0.1", "remark-gfm": "^3.0.1",

View file

@ -0,0 +1,118 @@
import Page from 'shared/components/wrappers/page.js'
import useApp from 'site/hooks/useApp.js'
import strapiLoader from 'shared/strapi/loader'
import { posts } from 'site/prebuild/strapi.blog.en.js'
import TimeAgo from 'react-timeago'
import MdxWrapper from 'shared/components/wrappers/mdx'
import Markdown from 'react-markdown'
const strapi = "https://posts.freesewing.org"
const Author = ({ author }) => (
<div id="author" className="flex flex-col lg:flex-row m-auto p-2 items-center">
<div className="theme-gradient w-40 h-40 p-2 rounded-full aspect-square hidden lg:block">
<div
className={`
w-lg bg-cover bg-center rounded-full aspect-square
hidden lg:block
`}
style={{backgroundImage: `url(${strapi}${author?.img})`}}
>
</div>
</div>
<div className="theme-gradient p-2 rounded-full aspect-square w-40 h-40 lg:hidden m-auto">
<img
className={`block w-full h-full mx-auto rounded-full`}
src={`${strapi}${author?.img}`}
alt={author?.displayname}
width={author?.picture?.width}
height={author?.picture?.height}
/>
</div>
<div className={`
text-center p-2 px-4 rounded-r-lg bg-opacity-50
lg:text-left
`}
>
<p className="text-xl">
<span className="font-semibold"> {author?.displayname}</span>
<span className="text-sm pl-2 opacity-70">Wrote this</span>
</p>
<div className="prose mdx">
<Markdown>{author?.about}</Markdown>
</div>
</div>
</div>
)
const PostPage = ({ post, author }) => {
const app = useApp()
return (
<Page app={app} title={post.title}>
<article className="mb-12">
<div className="flex flex-row justify-between text-sm mb-1 mt-2">
<span><TimeAgo date={post.date} /> [{post.date}]</span>
<span>
By <a
href="#author"
className="text-secondary hover:text-secondary-focus"
>
{author?.displayname || 'FIXME: No displayname'}
</a>
</span>
</div>
<figure>
<img
src={`${strapi}${post.image.formats.large.url}`}
alt={post.caption}
className="shadow m-auto"
/>
<figcaption
className="text-center mb-8 prose m-auto"
dangerouslySetInnerHTML={{__html: post.caption}}
/>
</figure>
<div className="strapi prose lg:prose-lg mb-12 m-auto">
<MdxWrapper mdx={post.mdx} app={app} />
</div>
<div className="max-w-prose text-lg lg:text-xl">
<Author author={author} />
</div>
</article>
</Page>
)
return (
<Page app={app} title='Blog' slug='blog'>
<article className="mb-12">
<div className="strapi prose lg:prose-lg mb-12 m-auto">
<MdxWrapper mdx={props.post.mdx} />
</div>
</article>
<Author author={author} type={props.type} t={props.t}/>
<pre>{JSON.stringify(props, null, 2)}</pre>
</Page>
)
}
export const getStaticProps = async (props) => {
const { post, author } = await strapiLoader('en', 'dev', 'blog', props.params.slug)
return { props: { post, author, slug: `blog/${props.params.slug}` } }
}
export const getStaticPaths = async () => {
const paths = []
for (const post of posts) paths.push({
params: {slug: post.slug}
})
return {
paths,
fallback: false,
}
}
export default PostPage

View file

@ -0,0 +1,61 @@
import Page from 'shared/components/wrappers/page.js'
import useApp from 'site/hooks/useApp.js'
import Link from 'next/link'
import { posts } from 'site/prebuild/strapi.blog.en.js'
import orderBy from 'lodash.orderby'
import TimeAgo from 'react-timeago'
const strapi = "https://posts.freesewing.org"
const Preview = ({ app, post }) => (
<div className="theme-gradient p-1 hover:p-0 hover:mb-1 hover:pointer transition-all">
<Link href={`/blog/${post.slug}`}>
<a title={post.title} className="hover:underline">
<div className="bg-base-100 w-full aspect-video shadow flex flex-column items-end" style={{
backgroundImage: `url(${strapi}${post.img})`,
backgroundSize: 'cover',
}}>
<div className="grow"></div>
<div className="text-right">
<div className={`
bg-neutral text-neutral-content
bg-opacity-80
px-4 text-right
`}>
<h5 className={`
text-neutral-content
text-xl font-normal
md:text-2xl md:font-light
`}>
{post.title}
</h5>
<p className={`
m-0 p-1 -mt-2
text-neutral-content
opacity-50
leading-normal text-sm font-normal
`}>
<TimeAgo date={post.date} /> by {post.author}
</p>
</div>
</div>
</div>
</a>
</Link>
</div>
)
export default (props) => {
const app = useApp()
return (
<Page app={app} title='FreeSewing Development Blog' slug='blog'>
<div className="grid grid-cols-1 gap-4 lg:grid-cols-2">
{Object.values(orderBy(posts, ['date'], ['desc']))
.map(post => <Preview app={app} post={post} />)
}
</div>
</Page>
)
}

View file

@ -6,8 +6,6 @@ export default (props) => {
const app = useApp() const app = useApp()
return ( return (
<Page app={app} title='FIXME: Create homepage content'> <Page app={app} title='FIXME: Create homepage content'>
<Logo size={200} theme={app.theme}/>
<button className="btn btn-primary" onClick={app.togglePrimaryMenu}>toggle menu</button>
</Page> </Page>
) )
} }

View file

@ -0,0 +1,7 @@
{
"slug": "joostdecock",
"name": "joost",
"displayname": "Joost De Cock",
"about": "Joost is FreeSewing's maintainer. As an introvert, he enjoys making clothes and shoes since you don't have to leave the house to do so 🙈\n\nYou can follow him as [j__st on Twitter](https://twitter.com/j__st) or [joostdecock on Github](https://github.com/joostdecock).",
"img": "/uploads/medium_tumblr_ok2vhi_YC_Dh1qbhhcgo1_1280_41d8d5799e.jpg"
}

View file

@ -1,95 +0,0 @@
export const authors = {
"joostdecock": {
"localizations": [],
"_id": "60eae49cdb32b45d5c4d6d93",
"about": "Joost is FreeSewing's maintainer. As an introvert, he enjoys making clothes and shoes since you don't have to leave the house to do so 🙈\n\nYou can follow him as [j__st on Twitter](https://twitter.com/j__st) or [joostdecock on Github](https://github.com/joostdecock).",
"name": "joost",
"displayname": "Joost De Cock",
"createdAt": "2021-07-11T12:31:24.779Z",
"updatedAt": "2021-09-19T14:23:43.748Z",
"__v": 0,
"picture": {
"_id": "6134ca45cd9dc073cc08db6a",
"name": "tumblr_ok2vhiYCDh1qbhhcgo1_1280.jpg",
"alternativeText": "Joost De Cock",
"caption": "Joost De Cock",
"hash": "tumblr_ok2vhi_YC_Dh1qbhhcgo1_1280_41d8d5799e",
"ext": ".jpg",
"mime": "image/jpeg",
"size": 76.85,
"width": 1080,
"height": 810,
"url": "/uploads/tumblr_ok2vhi_YC_Dh1qbhhcgo1_1280_41d8d5799e.jpg",
"formats": {
"thumbnail": {
"name": "thumbnail_tumblr_ok2vhiYCDh1qbhhcgo1_1280.jpg",
"hash": "thumbnail_tumblr_ok2vhi_YC_Dh1qbhhcgo1_1280_41d8d5799e",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 208,
"height": 156,
"size": 6.22,
"path": null,
"url": "/uploads/thumbnail_tumblr_ok2vhi_YC_Dh1qbhhcgo1_1280_41d8d5799e.jpg"
},
"large": {
"name": "large_tumblr_ok2vhiYCDh1qbhhcgo1_1280.jpg",
"hash": "large_tumblr_ok2vhi_YC_Dh1qbhhcgo1_1280_41d8d5799e",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 1000,
"height": 750,
"size": 85.96,
"path": null,
"url": "/uploads/large_tumblr_ok2vhi_YC_Dh1qbhhcgo1_1280_41d8d5799e.jpg"
},
"medium": {
"name": "medium_tumblr_ok2vhiYCDh1qbhhcgo1_1280.jpg",
"hash": "medium_tumblr_ok2vhi_YC_Dh1qbhhcgo1_1280_41d8d5799e",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 600,
"height": 450,
"size": 36,
"path": null,
"url": "/uploads/medium_tumblr_ok2vhi_YC_Dh1qbhhcgo1_1280_41d8d5799e.jpg"
},
"small": {
"name": "small_tumblr_ok2vhiYCDh1qbhhcgo1_1280.jpg",
"hash": "small_tumblr_ok2vhi_YC_Dh1qbhhcgo1_1280_41d8d5799e",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 400,
"height": 300,
"size": 17.49,
"path": null,
"url": "/uploads/small_tumblr_ok2vhi_YC_Dh1qbhhcgo1_1280_41d8d5799e.jpg"
},
"xsmall": {
"name": "xsmall_tumblr_ok2vhiYCDh1qbhhcgo1_1280.jpg",
"hash": "xsmall_tumblr_ok2vhi_YC_Dh1qbhhcgo1_1280_41d8d5799e",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 200,
"height": 150,
"size": 5.99,
"path": null,
"url": "/uploads/xsmall_tumblr_ok2vhi_YC_Dh1qbhhcgo1_1280_41d8d5799e.jpg"
}
},
"provider": "local",
"related": [
"60eae49cdb32b45d5c4d6d93",
"60fade591972003530e03b39",
"60fadf5e19815b356b32781e"
],
"createdAt": "2021-09-05T13:46:45.753Z",
"updatedAt": "2021-09-19T14:24:00.480Z",
"__v": 0,
"id": "6134ca45cd9dc073cc08db6a"
},
"locale": "en",
"slug": "joostdecock",
"id": "60eae49cdb32b45d5c4d6d93"
}
}

View file

@ -0,0 +1,87 @@
{
"dev": true,
"localizations": [],
"_id": "60eb1d41db32b45d5c4d6d9c",
"body": "Since we're changing pretty much everything in [project 2022](/blog/project-2022) anyway, we might as well try to reduce our bundle size by removing our dependency on [Material UI](https://material-ui.com/), the React component framework that implements Google's material design.\n\nMaterialUI is a large project, and it makes up a significant portion of the so-called *bundle size*, the amount of Javascript code we ship to the browser.\n\nObviously, for that size, MaterialUI brings a lot to the table, and it remains to be seen whether we can build everything without it.\n\nStill, I want to try, and I've got a secret little helper in the form of [DaisyUI](https://daisyui.com/).\n\nBefore you think that we're swapping out one component library for another, DaisyUI works with TailwindCSS and the components it provides are CSS only. So no extra Javascript to ship 🎉\n\n## Can I haz themes?\n\nAnother thing that DaisyUI brings to the table is support for themeing. While TailwindCSS recently added support for dark mode, theming in DaisyUI is more flexible, allowing us to not only have dark and light versions of the site, but also a bunch of other themes.\n\nFor someone who gets bored easily like myself, that sounds great 😃\n",
"caption": "A daisy - Picture by Bess Hamiti via Pexels",
"slug": "daisyui-components-themes",
"date": "2021-07-07",
"title": "Once more without MaterialUI: DaisyUI for CSS components and themes",
"linktitle": "Components and themes with DaisyUI",
"published_at": "2021-07-11T16:33:09.567Z",
"createdAt": "2021-07-11T16:33:05.698Z",
"updatedAt": "2021-07-23T15:04:53.670Z",
"__v": 0,
"author": "joostdecock",
"image": {
"_id": "60eb1d29db32b45d5c4d6d9b",
"name": "pexels-bess-hamiti-36764.jpg",
"alternativeText": "",
"caption": "",
"hash": "pexels_bess_hamiti_36764_b2c736ca98",
"ext": ".jpg",
"mime": "image/jpeg",
"size": 144.55,
"width": 1980,
"height": 1289,
"url": "/uploads/pexels_bess_hamiti_36764_b2c736ca98.jpg",
"formats": {
"thumbnail": {
"name": "thumbnail_pexels-bess-hamiti-36764.jpg",
"hash": "thumbnail_pexels_bess_hamiti_36764_b2c736ca98",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 240,
"height": 156,
"size": 4.91,
"path": null,
"url": "/uploads/thumbnail_pexels_bess_hamiti_36764_b2c736ca98.jpg"
},
"large": {
"name": "large_pexels-bess-hamiti-36764.jpg",
"hash": "large_pexels_bess_hamiti_36764_b2c736ca98",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 1000,
"height": 651,
"size": 42.87,
"path": null,
"url": "/uploads/large_pexels_bess_hamiti_36764_b2c736ca98.jpg"
},
"medium": {
"name": "medium_pexels-bess-hamiti-36764.jpg",
"hash": "medium_pexels_bess_hamiti_36764_b2c736ca98",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 750,
"height": 488,
"size": 26.51,
"path": null,
"url": "/uploads/medium_pexels_bess_hamiti_36764_b2c736ca98.jpg"
},
"small": {
"name": "small_pexels-bess-hamiti-36764.jpg",
"hash": "small_pexels_bess_hamiti_36764_b2c736ca98",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 500,
"height": 326,
"size": 14.45,
"path": null,
"url": "/uploads/small_pexels_bess_hamiti_36764_b2c736ca98.jpg"
}
},
"provider": "local",
"related": [
"60eb1d41db32b45d5c4d6d9c"
],
"createdAt": "2021-07-11T16:32:41.273Z",
"updatedAt": "2021-07-11T16:33:05.741Z",
"__v": 0,
"id": "60eb1d29db32b45d5c4d6d9b"
},
"site": "60ead51670e8d35a6d089fa0",
"locale": "en",
"id": "60eb1d41db32b45d5c4d6d9c",
"intro": "Since we're changing pretty much everything in project 2022 anyway, we might as well try to reduce our bundle size by removing our dependency on Material UI , the React component framework that implements Google's material design."
}

View file

@ -0,0 +1,98 @@
{
"dev": true,
"localizations": [],
"_id": "60f2e2ef2f81c03239366844",
"body": "I just made a bunch of breaking changes in `@freesewing/i18n` and it made me realize that if/when we get around to merging my work back into our monorepo, there's going to be too many changes not to bump the major version.\n\nAnd I guess that with that I've made the realization that I'm working on FreeSewing v3.\n\nThe breaking changes are not in our core library or how patterns work, but more in how we organize our code and tie it all together. Ultimately, I want to make it easier for people to graduate from interested bystander to contributor, and bringing everything into our monorepo with reasonable dependencies is part of that.\n\n## Changes in i18n\n\nTake our internationalization package, [@freesewing/i18n](https://www.npmjs.com/package/@freesewing/i18n) which holds all our translations.\n\nBecause of this, it's a relatively large package, and weighs in just over 282.9kB when minified. That is 4 times the size of our core library.\n\nIt's not a problem as such. There's no shortcut for all this translated strings, we need to ship them somehow. But the problem is that the package is currently not *tree-shakeable*.\n\nTree-shaking is a term that is used to describe a process of removing unused code from the Javascript bundle, and is implemented by bundlers such as Rollup or Webpack.\nThe idea is that if you use only a part of a library, the rest of it will be kept out of the bundle, so you have to ship less code.\n\nWith the current setup, `@freesewing/i18n` is not tree-shakeable, which means that if you build a website that is exclusively English, you still will find all other translations in the bundle. Not cool.\n\nCurrently, `i18n` has a default export which is an object with these keys: \n\n```js\nexport { strings, languages, plugin, jargon }\n```\n\nIn v3, we will switch to named exports, and export the following:\n\n - `locales`: List of languages codes we provide\n - `languages`: Translations for the languages codes (for use in a language switcher)\n - `en`: English translations\n - `es`: Spanish translations\n - `de`: German translations\n - `fr`: French translations\n - `nl`: Dutch translations\n - `jargon_en`: English jargon file (for use in our [remark plugin for jargon](https://www.npmjs.com/package/remark-jargon))\n - `jargon_es`: Spanish jargon file (for use in our [remark plugin for jargon](https://www.npmjs.com/package/remark-jargon))\n - `jargon_de`: German jargon file (for use in our [remark plugin for jargon](https://www.npmjs.com/package/remark-jargon))\n - `jargon_fr`: French jargon file (for use in our [remark plugin for jargon](https://www.npmjs.com/package/remark-jargon))\n - `jargon_nl`: Dutch jargon file (for use in our [remark plugin for jargon](https://www.npmjs.com/package/remark-jargon))\n\n\nNow you can import only what you need, and let your bundler tree-shake the rest.\n\n\n\n\n",
"caption": "Photo by Miguel Á. Padriñán via Pexels",
"slug": "freesewing-v3-seems-inevitable",
"date": "2021-07-16",
"title": "Better tree-shaking means breaking changes; FreeSewing v3 now seems inevitable",
"linktitle": "FreeSewing v3 seems inevitable",
"published_at": "2021-07-17T15:15:08.840Z",
"createdAt": "2021-07-17T14:02:23.828Z",
"updatedAt": "2021-07-23T15:04:23.665Z",
"__v": 0,
"author": "joostdecock",
"image": {
"_id": "60f2de802f81c03239366843",
"name": "pexels-miguel-á-padriñán-1061140.jpg",
"alternativeText": "",
"caption": "",
"hash": "pexels_miguel_a_padrinan_1061140_7a14d61e89",
"ext": ".jpg",
"mime": "image/jpeg",
"size": 161.74,
"width": 1920,
"height": 1276,
"url": "/uploads/pexels_miguel_a_padrinan_1061140_7a14d61e89.jpg",
"formats": {
"thumbnail": {
"name": "thumbnail_pexels-miguel-á-padriñán-1061140.jpg",
"hash": "thumbnail_pexels_miguel_a_padrinan_1061140_7a14d61e89",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 235,
"height": 156,
"size": 2.6,
"path": null,
"url": "/uploads/thumbnail_pexels_miguel_a_padrinan_1061140_7a14d61e89.jpg"
},
"large": {
"name": "large_pexels-miguel-á-padriñán-1061140.jpg",
"hash": "large_pexels_miguel_a_padrinan_1061140_7a14d61e89",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 1000,
"height": 665,
"size": 37.15,
"path": null,
"url": "/uploads/large_pexels_miguel_a_padrinan_1061140_7a14d61e89.jpg"
},
"medium": {
"name": "medium_pexels-miguel-á-padriñán-1061140.jpg",
"hash": "medium_pexels_miguel_a_padrinan_1061140_7a14d61e89",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 600,
"height": 399,
"size": 11.93,
"path": null,
"url": "/uploads/medium_pexels_miguel_a_padrinan_1061140_7a14d61e89.jpg"
},
"small": {
"name": "small_pexels-miguel-á-padriñán-1061140.jpg",
"hash": "small_pexels_miguel_a_padrinan_1061140_7a14d61e89",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 400,
"height": 266,
"size": 5.54,
"path": null,
"url": "/uploads/small_pexels_miguel_a_padrinan_1061140_7a14d61e89.jpg"
},
"xsmall": {
"name": "xsmall_pexels-miguel-á-padriñán-1061140.jpg",
"hash": "xsmall_pexels_miguel_a_padrinan_1061140_7a14d61e89",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 200,
"height": 133,
"size": 2.16,
"path": null,
"url": "/uploads/xsmall_pexels_miguel_a_padrinan_1061140_7a14d61e89.jpg"
}
},
"provider": "local",
"related": [
"60f2e2ef2f81c03239366844"
],
"createdAt": "2021-07-17T13:43:28.919Z",
"updatedAt": "2021-07-17T14:02:23.831Z",
"__v": 0,
"id": "60f2de802f81c03239366843"
},
"site": "60ead51670e8d35a6d089fa0",
"locale": "en",
"id": "60f2e2ef2f81c03239366844",
"intro": "I just made a bunch of breaking changes in @freesewing/i18n and it made me realize that if/when we get around to merging my work back into our monorepo, there's going to be too many changes not to bump the major version."
}

View file

@ -0,0 +1,87 @@
{
"dev": true,
"localizations": [],
"_id": "60eb2759db32b45d5c4d6da2",
"body": "\nSince we've got a ton of documentation, good search is a crucial aspect of our websites.\n\nIn [project 2022](/blog/project-2022) I wanted to improve the search experience for our users. Here are some of the pain points I have identified:\n\n - Searching takes too many clicks\n - All content is indexed for search, when people are typically looking for documentation\n\nI've implemented a new search solution that is never more than one click away (right there in the navigation bar). And, even better, can be triggered at any time by pressing `Ctrl-k` on your keyboard.\n\nThis will launch the search and auto-focus the input field so you can just start typing. The first result will be selected, but you can navigation through the results using the up and down arrows, giving you full control over the search experience from the keyboard.\n\nIf you want to cancel the search, clicking `escape` will do just that. To clear the search input, `ctrl-c` is your friend.\n\nAll of this keyboard-drive search adds little value on mobile. But it's particularly handy when you're working on a pattern or documentation and you quickly want to look something up.\n\nWhile we still have to handle the search indexing itself (we're using the index of the current freesewing.dev site since we did not change any URLs), I feel the search experience itself is a nice improvement.\n\nGive it a go and let me know what you think [in our #project-2022 Discord channel](https://discord.freesewing.org/).",
"caption": "Picture by Skitterphoto via Pexels",
"slug": "improved-search-keyboard",
"date": "2021-07-09",
"title": "Improved search with keyboard bindings",
"linktitle": "Improved search with keyboard bindings",
"published_at": "2021-07-11T17:16:16.082Z",
"createdAt": "2021-07-11T17:16:09.119Z",
"updatedAt": "2021-07-23T15:04:40.641Z",
"__v": 0,
"author": "joostdecock",
"image": {
"_id": "60eb2742db32b45d5c4d6da1",
"name": "pexels-skitterphoto-63901.jpg",
"alternativeText": "",
"caption": "",
"hash": "pexels_skitterphoto_63901_6dad2c294b",
"ext": ".jpg",
"mime": "image/jpeg",
"size": 150.68,
"width": 1920,
"height": 1302,
"url": "/uploads/pexels_skitterphoto_63901_6dad2c294b.jpg",
"formats": {
"thumbnail": {
"name": "thumbnail_pexels-skitterphoto-63901.jpg",
"hash": "thumbnail_pexels_skitterphoto_63901_6dad2c294b",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 230,
"height": 156,
"size": 5.91,
"path": null,
"url": "/uploads/thumbnail_pexels_skitterphoto_63901_6dad2c294b.jpg"
},
"large": {
"name": "large_pexels-skitterphoto-63901.jpg",
"hash": "large_pexels_skitterphoto_63901_6dad2c294b",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 1000,
"height": 678,
"size": 48.18,
"path": null,
"url": "/uploads/large_pexels_skitterphoto_63901_6dad2c294b.jpg"
},
"medium": {
"name": "medium_pexels-skitterphoto-63901.jpg",
"hash": "medium_pexels_skitterphoto_63901_6dad2c294b",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 750,
"height": 509,
"size": 30.98,
"path": null,
"url": "/uploads/medium_pexels_skitterphoto_63901_6dad2c294b.jpg"
},
"small": {
"name": "small_pexels-skitterphoto-63901.jpg",
"hash": "small_pexels_skitterphoto_63901_6dad2c294b",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 500,
"height": 339,
"size": 17.53,
"path": null,
"url": "/uploads/small_pexels_skitterphoto_63901_6dad2c294b.jpg"
}
},
"provider": "local",
"related": [
"60eb2759db32b45d5c4d6da2"
],
"createdAt": "2021-07-11T17:15:46.611Z",
"updatedAt": "2021-07-11T17:16:09.123Z",
"__v": 0,
"id": "60eb2742db32b45d5c4d6da1"
},
"site": "60ead51670e8d35a6d089fa0",
"locale": "en",
"id": "60eb2759db32b45d5c4d6da2",
"intro": "Since we've got a ton of documentation, good search is a crucial aspect of our websites."
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,87 @@
{
"dev": true,
"localizations": [],
"_id": "60f0616ddb32b45d5c4d6dac",
"body": "I wanted to do a quick write-up of the main layout blocks of our websites. So let's have a look:\n\n## Screen sizes and break points: 1024px wide or not?\n\nOur websites should work on a variety of devices:\n![screens.png](https://posts.freesewing.org/uploads/screens_cf77bf2abb.png)\n\nFor this, we practice so-called **responsive design**. A design that adapts to the screen size. \nOr rather, the screen width because thanks to scrolling, we always have plenty of height.\n\nIn practice, we use so-called **break points**. A list of one or more screen widths at which we apply a different layout for our website.\nTo keep things simple, we utilize a single breakpoint: **1024px**. \n\n - large screen: 1024 pixels or more wide\n - small screen: less than 1024 pixels wide\n\n## Page structure: Three elements, one dimension\n\nThe structure of each page on the website is a vertical column with three elements:\n\n - The header which holds the so-called `navbar`, short for *navigation bar*\n - The main part of the page, which in this case shows this blog post\n - The footer\n\nIt looks like this:\n\n![spage.png](https://posts.freesewing.org/uploads/spage_db9ce89cb5.png)\n\nThat's on large screens. We'll have a closer look at small screens later.\n\n## Footer\n\nThe footer is the simplest element. It's always there, and it sits at the bottom of the page.\n\nThe footer spans the entire width of the screen, but the content within is constrained by a maximum width. That width is **1536px** (indicated by the red dashed lines in the image above).\nThis is to avoid that people on a 4K monitor get neck cramps like they're attending some tennis match, just to read what's on the screen.\n\n## Navbar\n\nThe navbar is similar to the footer in the sense that it spans the full width, yet the content within is constrained to maximum 1536 pixels.\n\nOn large screens, the navbar sits at the top of the page and does not have a background color to make it stand out.\nThat's because it's rather intuitive to have the navigation at the top of the page.\n\nOn mobile, the navbar collapses gets fixed at the bottom of the screen, and gets a background color to draw attention to the fact that this is where the navigation controls are:\n\n![mpage.png](https://posts.freesewing.org/uploads/mpage_e3cade7d29.png)\n\nThe reason we place the navigation at the bottom of the screen is that it's easier to reach when using a mobile phone with one hand.\n\nRather than try to cram everything in the (now very small) navbar. We simply use a button that makes the whole screen the navigation menu on small screens.\n\n## Main layout\n\nThe *main* part of a page can be pretty much anything we want. But on most pages, it looks like this:\n\n![slayout.png](https://posts.freesewing.org/uploads/slayout_50f7b8270b.png)\n\nHere's too we are constraining the content to the same maximum width.\nApart from that, we have the main content of the page, and the `aside` which typically holds navigation.\n\nThis aside is not available on mobile, and instead can be reached by bringing up the menu.\n\n\n## Summary\n\nAll our websites look the same\n\n - The page is a vertical column made of 3 parts: header -> main -> footer\n - The main part can be any layout, but the most common has content + aside\n - On mobile, the navbar sits fixed at the bottom, and aside moves off-canvas\n\n",
"caption": " Photo by Kelly Lacy via Pexels",
"slug": "layout-blocks-overview",
"date": "2021-07-15",
"title": "A quick tour of the main layout blocks",
"linktitle": "Main layout blocks",
"published_at": "2021-07-15T16:25:20.922Z",
"createdAt": "2021-07-15T16:25:17.007Z",
"updatedAt": "2021-07-23T15:04:30.866Z",
"__v": 0,
"author": "joostdecock",
"image": {
"_id": "60f05ac3db32b45d5c4d6da7",
"name": "pexels-kelly-lacy-2402233.jpg",
"alternativeText": "",
"caption": "",
"hash": "pexels_kelly_lacy_2402233_45b5683839",
"ext": ".jpg",
"mime": "image/jpeg",
"size": 630.2,
"width": 1920,
"height": 1078,
"url": "/uploads/pexels_kelly_lacy_2402233_45b5683839.jpg",
"formats": {
"thumbnail": {
"name": "thumbnail_pexels-kelly-lacy-2402233.jpg",
"hash": "thumbnail_pexels_kelly_lacy_2402233_45b5683839",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 245,
"height": 138,
"size": 16.81,
"path": null,
"url": "/uploads/thumbnail_pexels_kelly_lacy_2402233_45b5683839.jpg"
},
"large": {
"name": "large_pexels-kelly-lacy-2402233.jpg",
"hash": "large_pexels_kelly_lacy_2402233_45b5683839",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 1000,
"height": 561,
"size": 207.61,
"path": null,
"url": "/uploads/large_pexels_kelly_lacy_2402233_45b5683839.jpg"
},
"medium": {
"name": "medium_pexels-kelly-lacy-2402233.jpg",
"hash": "medium_pexels_kelly_lacy_2402233_45b5683839",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 750,
"height": 421,
"size": 122.96,
"path": null,
"url": "/uploads/medium_pexels_kelly_lacy_2402233_45b5683839.jpg"
},
"small": {
"name": "small_pexels-kelly-lacy-2402233.jpg",
"hash": "small_pexels_kelly_lacy_2402233_45b5683839",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 500,
"height": 281,
"size": 61.63,
"path": null,
"url": "/uploads/small_pexels_kelly_lacy_2402233_45b5683839.jpg"
}
},
"provider": "local",
"related": [
"60f0616ddb32b45d5c4d6dac"
],
"createdAt": "2021-07-15T15:56:51.570Z",
"updatedAt": "2021-07-15T16:25:17.017Z",
"__v": 0,
"id": "60f05ac3db32b45d5c4d6da7"
},
"site": "60ead51670e8d35a6d089fa0",
"locale": "en",
"id": "60f0616ddb32b45d5c4d6dac",
"intro": "I wanted to do a quick write-up of the main layout blocks of our websites. So let's have a look:"
}

View file

@ -0,0 +1,97 @@
{
"dev": true,
"localizations": [],
"_id": "612785b5fca7f36c6a727a6b",
"body": "Effective immediately, all blog and showcase posts on freesewing.org are backed by [our Strapi instance](https://posts.freesewing.org/), rather than [our markdown repository](https://github.com/freesewing/markdown). That markdown repository has been archived, and the markdown content that are not blog or showcase posts (predominantly documentation) has been moved into [our monorepo](https://github.com/freesewing/freesewing).\n\n## These changes have already been deployed\n\nBoth freesewing.org and freesewing.dev have been updated to reflect this new reality. That includes pulling blog and showcase posts from strapi, but we also replaced the markdown submodule with our monorepo to access the markdown content.\n\n## Impact on translation\n\nThis change also has an impact on translations, as we used to have two crowdin projects, one for the markdown content (linked to the markdown repository) and another for the strings (linked to our monorepo). \n\nNow that all this content in hosted in our monorepo, I have merged these projects, so that we now have a single Crowdin project that's available via [translate.freesewing.org](https://translate.freesewing.org/).\n\nWhile that consolidates translation on Crowdin, we now have a different split since content hosted on Strapi must be translated there. However, translation of blog posts and showcase posts is a lot less crucial than translation of the documentation and strings.\n\nIn addition, this also opens the door for having original non-English content. For example, a French blog post could be written that does not need to be available in English. This is a departure of the tight coupling of English (as the origin language) and the translations for this kind of language, and opens the door for the various linguistic communities to make our blogs more *their own* by providing original content.\n\n## Impact on content creation\n\nPeople looking to create blog posts or showcases should now use Strapi to do so.\nThe [documentation for editors](https://freesewing.dev/editors/howtos/) has already been updated.\n\nIf you want a Strapi account, please let us know [on our Discord server](https://discord.freesewing.org)\n\n## Thanks, I hate it\n\nThese are the first changes to come out of [project 2022](/blog/project-2022) and they impact the way contributors & translators work, which is potentially frustrating to people.\n\nApart from doing my work on project 2022 in the open, I've also tried to create some buy-in by adding a discussion on these changes to the [agenda of the contributor call of 3 weeks ago](https://github.com/freesewing/freesewing/issues/1241). \n\nAs that agenda item was not picked up, it is possible that people feel blind-sighted by these changes. If that is the case, feel free to reach out to me to discuss where I'm going with this.",
"caption": "This migration marks the first important changes to come out of project 2022",
"slug": "migration-to-strapi",
"date": "2021-08-26",
"title": "We migrated to strapi and moved our markdown content into our monorepo",
"linktitle": "Migration to Strapi",
"locale": "en",
"published_at": "2021-08-26T12:41:37.022Z",
"createdAt": "2021-08-26T12:14:45.394Z",
"updatedAt": "2021-08-26T12:41:37.039Z",
"__v": 0,
"author": "joostdecock",
"image": {
"_id": "61276adbfca7f36c6a727a6a",
"name": "pexels-pixabay-315939.jpg",
"alternativeText": "",
"caption": "",
"hash": "pexels_pixabay_315939_c82497916e",
"ext": ".jpg",
"mime": "image/jpeg",
"size": 279.85,
"width": 1920,
"height": 1280,
"url": "/uploads/pexels_pixabay_315939_c82497916e.jpg",
"formats": {
"thumbnail": {
"name": "thumbnail_pexels-pixabay-315939.jpg",
"hash": "thumbnail_pexels_pixabay_315939_c82497916e",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 234,
"height": 156,
"size": 9.17,
"path": null,
"url": "/uploads/thumbnail_pexels_pixabay_315939_c82497916e.jpg"
},
"large": {
"name": "large_pexels-pixabay-315939.jpg",
"hash": "large_pexels_pixabay_315939_c82497916e",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 1000,
"height": 667,
"size": 97.01,
"path": null,
"url": "/uploads/large_pexels_pixabay_315939_c82497916e.jpg"
},
"medium": {
"name": "medium_pexels-pixabay-315939.jpg",
"hash": "medium_pexels_pixabay_315939_c82497916e",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 600,
"height": 400,
"size": 43.19,
"path": null,
"url": "/uploads/medium_pexels_pixabay_315939_c82497916e.jpg"
},
"small": {
"name": "small_pexels-pixabay-315939.jpg",
"hash": "small_pexels_pixabay_315939_c82497916e",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 400,
"height": 267,
"size": 22.21,
"path": null,
"url": "/uploads/small_pexels_pixabay_315939_c82497916e.jpg"
},
"xsmall": {
"name": "xsmall_pexels-pixabay-315939.jpg",
"hash": "xsmall_pexels_pixabay_315939_c82497916e",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 200,
"height": 133,
"size": 7.14,
"path": null,
"url": "/uploads/xsmall_pexels_pixabay_315939_c82497916e.jpg"
}
},
"provider": "local",
"related": [
"612785b5fca7f36c6a727a6b"
],
"createdAt": "2021-08-26T10:20:11.355Z",
"updatedAt": "2021-08-26T12:14:45.398Z",
"__v": 0,
"id": "61276adbfca7f36c6a727a6a"
},
"id": "612785b5fca7f36c6a727a6b",
"intro": "Effective immediately, all blog and showcase posts on freesewing.org are backed by our Strapi instance , rather than our markdown repository . That markdown repository has been archived, and the markdown content that are not blog or showcase posts (predominantly documentation) has been moved into our monorepo ."
}

View file

@ -0,0 +1,97 @@
{
"dev": true,
"localizations": [],
"_id": "6128ccacfca7f36c6a727a70",
"title": "Auto-generated images showing option impact to improve documentation for pattern options",
"date": "2021-08-27",
"linktitle": "Sampling pattern options for bettter docs",
"slug": "pattern-docs-option-sampling",
"caption": "Much like these steps, intervals in sampled options form a rainbow in the auto-generated images",
"body": "Yesterday, I merged our markdown content into our monorepo, and archived our markdown repository. Our documentation now lives side-by-side with our code, and that opens up some options that previously would have been cumbersome to implement. \n\nToday, I went ahead and added auto-generated images to the documentation page for all pattern options. The idea is simple: For each option, we [sample the option](https://freesewing.dev/reference/api/pattern/sampleoption/) which generates an SVG image like the one below:\n\n![aaron_chestease_sample.svg](https://posts.freesewing.org/uploads/aaron_chestease_sample_5600e96385.svg)\n\nWe currently have 761 pattern options, so after generating the image, I went ahead and added it at the bottom of each of the documentation pages.\n\n## No more shared option pages\n\nTo make this possible, I first had to de-dupe a bunch of documentation pages that were shared between various patterns. Patterns that extend another pattern often share a bunch of options, and initially I added logic to the frontend code to avoid having to create all this documenation.\n\nHowever, this added logic makes it harder for people to wrap their head around our frontend, and understand how things work (where is this documentation coming from?). It also meant that sometimes the description of the option was poorly matched to the pattern. For example, the documentation would talk about a *block* because the option documentation is re-used from Brian, when the user is browsing documentation for a different design that happens to extend Brian and use this option.\n\nI had to add a bunch of new markdown files and folders for this, but it will make organisation of the documentation more straight-forward, and in general simpler to *get* how the website works, which was a goal I set for myself in [project 2022](/blog/project-2022).\n\nAnd while in principle this would add a bunch of translation work, these are all strings that have been translated before, so I triggered an automatic translation based on the so-called *translation memory* in Crowdin. (This translation memory is a list of strings and their translations that have been translated and approved inside the project).\n\nIf you see some pattern options where this doesn't work, or looks weird, [let me know](https://discord.freesewing.org/).\n",
"locale": "en",
"published_at": "2021-08-27T11:31:14.661Z",
"createdAt": "2021-08-27T11:29:48.548Z",
"updatedAt": "2021-08-27T12:05:59.065Z",
"__v": 0,
"image": {
"_id": "6128cce1fca7f36c6a727a72",
"name": "pexels-george-becker-122480.jpg",
"alternativeText": "",
"caption": "",
"hash": "pexels_george_becker_122480_5e1723471f",
"ext": ".jpg",
"mime": "image/jpeg",
"size": 444.06,
"width": 1920,
"height": 1278,
"url": "/uploads/pexels_george_becker_122480_5e1723471f.jpg",
"formats": {
"thumbnail": {
"name": "thumbnail_pexels-george-becker-122480.jpg",
"hash": "thumbnail_pexels_george_becker_122480_5e1723471f",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 234,
"height": 156,
"size": 7.73,
"path": null,
"url": "/uploads/thumbnail_pexels_george_becker_122480_5e1723471f.jpg"
},
"large": {
"name": "large_pexels-george-becker-122480.jpg",
"hash": "large_pexels_george_becker_122480_5e1723471f",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 1000,
"height": 666,
"size": 109.82,
"path": null,
"url": "/uploads/large_pexels_george_becker_122480_5e1723471f.jpg"
},
"medium": {
"name": "medium_pexels-george-becker-122480.jpg",
"hash": "medium_pexels_george_becker_122480_5e1723471f",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 600,
"height": 399,
"size": 38.4,
"path": null,
"url": "/uploads/medium_pexels_george_becker_122480_5e1723471f.jpg"
},
"small": {
"name": "small_pexels-george-becker-122480.jpg",
"hash": "small_pexels_george_becker_122480_5e1723471f",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 400,
"height": 266,
"size": 18.42,
"path": null,
"url": "/uploads/small_pexels_george_becker_122480_5e1723471f.jpg"
},
"xsmall": {
"name": "xsmall_pexels-george-becker-122480.jpg",
"hash": "xsmall_pexels_george_becker_122480_5e1723471f",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 200,
"height": 133,
"size": 6.08,
"path": null,
"url": "/uploads/xsmall_pexels_george_becker_122480_5e1723471f.jpg"
}
},
"provider": "local",
"related": [
"6128ccacfca7f36c6a727a70"
],
"createdAt": "2021-08-27T11:30:41.808Z",
"updatedAt": "2021-08-27T11:31:12.950Z",
"__v": 0,
"id": "6128cce1fca7f36c6a727a72"
},
"author": "joostdecock",
"id": "6128ccacfca7f36c6a727a70",
"intro": "Yesterday, I merged our markdown content into our monorepo, and archived our markdown repository. Our documentation now lives side-by-side with our code, and that opens up some options that previously would have been cumbersome to implement."
}

View file

@ -0,0 +1,87 @@
{
"dev": true,
"localizations": [],
"_id": "60eb151cdb32b45d5c4d6d96",
"body": "During [my summer break](https://github.com/freesewing/freesewing/discussions/1139) I wanted to look into migrating the FreeSewing websites (freesewing.org and freesewing.dev) from [GatbsyJS](https://gatsbyjs.org) to [NextJS](https://nextjs.org).\n\nI've been [frustrated with Gatsby](https://twitter.com/j__st/status/1398987583644966912) for a while now, and I feel its complexity is a large hurdle for people to get more hands-on with our websites.\n\nMy main gripe is with Gatsby's build process. You essentially have to maintain two different projects: The website itself as it runs. And the infamous `gatbsy-node.js` that outlines everything required to build the site.\n\nIn comparison, NextJS handles this much better (IMHO) with their `getStaticProps` and `getStaticPaths` methods. Because they are defined at the page level, we can keep break up the complexity for an entire site build per page.\n\nI am going to start with the freesewing.dev website, and if that goes well, I'll have a look at (the much more complex) freesewing.org site.\n\nTo manage expectations, I'm calling this the **2022 project** because frankly, if this gets done by the end of the year, I'd be happy.\n\nIf you want to follow along, check out [my fork of the FreeSewing monorepo](https://github.com/joostdecock/freesewing) or [come hang out with us on Discord](https://discord.freesewing.org/) in the `#project-2022` temporary channel.",
"caption": "A person working in construction - Picture by by Anamul Rezwan via Pexels",
"slug": "project-2022",
"date": "2021-07-05",
"title": "Project 2022: Giving FreeSewing a new face",
"linktitle": "Announcing projet 2022",
"published_at": "2021-07-11T15:59:35.454Z",
"createdAt": "2021-07-11T15:58:20.218Z",
"updatedAt": "2021-07-23T15:05:11.967Z",
"__v": 0,
"author": "joostdecock",
"image": {
"_id": "60eb14eedb32b45d5c4d6d95",
"name": "pexels-anamul-rezwan-1216544.jpg",
"alternativeText": "",
"caption": "",
"hash": "pexels_anamul_rezwan_1216544_05424140f6",
"ext": ".jpg",
"mime": "image/jpeg",
"size": 196.29,
"width": 1920,
"height": 1280,
"url": "/uploads/pexels_anamul_rezwan_1216544_05424140f6.jpg",
"formats": {
"thumbnail": {
"name": "thumbnail_pexels-anamul-rezwan-1216544.jpg",
"hash": "thumbnail_pexels_anamul_rezwan_1216544_05424140f6",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 234,
"height": 156,
"size": 7.19,
"path": null,
"url": "/uploads/thumbnail_pexels_anamul_rezwan_1216544_05424140f6.jpg"
},
"large": {
"name": "large_pexels-anamul-rezwan-1216544.jpg",
"hash": "large_pexels_anamul_rezwan_1216544_05424140f6",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 1000,
"height": 667,
"size": 67.52,
"path": null,
"url": "/uploads/large_pexels_anamul_rezwan_1216544_05424140f6.jpg"
},
"medium": {
"name": "medium_pexels-anamul-rezwan-1216544.jpg",
"hash": "medium_pexels_anamul_rezwan_1216544_05424140f6",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 750,
"height": 500,
"size": 43.01,
"path": null,
"url": "/uploads/medium_pexels_anamul_rezwan_1216544_05424140f6.jpg"
},
"small": {
"name": "small_pexels-anamul-rezwan-1216544.jpg",
"hash": "small_pexels_anamul_rezwan_1216544_05424140f6",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 500,
"height": 333,
"size": 23.31,
"path": null,
"url": "/uploads/small_pexels_anamul_rezwan_1216544_05424140f6.jpg"
}
},
"provider": "local",
"related": [
"60eb151cdb32b45d5c4d6d96"
],
"createdAt": "2021-07-11T15:57:34.488Z",
"updatedAt": "2021-07-11T15:58:20.222Z",
"__v": 0,
"id": "60eb14eedb32b45d5c4d6d95"
},
"site": "60ead51670e8d35a6d089fa0",
"locale": "en",
"id": "60eb151cdb32b45d5c4d6d96",
"intro": "During my summer break I wanted to look into migrating the FreeSewing websites (freesewing.org and freesewing.dev) from GatbsyJS to NextJS ."
}

View file

@ -0,0 +1,87 @@
{
"dev": true,
"localizations": [],
"_id": "60eb237cdb32b45d5c4d6d9f",
"body": "Up until now, our websites each live in their own repository. This makes sharing code and content somewhat more involved, and situation that we currently handle as follows:\n\n- Content is shared by making [our markdown repository](https://github.com/freesewing/markdown) available as a git submodule\n- Code is shared by publishing packages on NPM, specifically [@freesewing/components](https://www.npmjs.com/package/@freesewing/components) for shared React components, [@freesewing/css-theme](https://www.npmjs.com/package/@freesewing/css-theme) for shared CSS,\nand [@freesewing/mui-theme](https://www.npmjs.com/package/@freesewing/mui-theme) for our shared MaterialUI theme.\n\nWhile that works fine, it has a few downsides:\n\n - Changes to the theming require publishing a new version of our software\n - The mix of library components and forntend components in [@freesewing/components](https://www.npmjs.com/package/@freesewing/components) make semantic versioning hard to apply\n\nThe first point is pretty straight-forward. The latter might require some more explanation:\n\nOur components package contains a number of components that we provide to users our our software, rather than users of our websites. Things like our `Draft` components, and `Workbench` component can be re-used by people who want to build their own projects/sites on top of FreeSewing.\n\nOn the other hand, there's a bunch of components that are clearly more geared towards internal use, such as the `Icon`, `Logo` or `Robots` components to name but a few.\n\nStrictly speaking, respecting [semantic versioning](https://semver.org/) means that a breaking changes in these components should trigger a new major version. But clearly it doesn't make sense to bump FreeSewing to a new major version just because we made a breaking change in our `Logo` component.\n\nIf we can bring our websites into our monorepo and share code directly in the repository, we can limit ourselves to publishing only those components that are not tightly coupled with our own website(s).\n\nSo how do we do that?\n\n# freesewing.dev in the monorepo\n\nFirst thing I did was bring freesewing.dev into our monorep. In `packages/freesewing.dev` to be precise. Then I added a new folder for shared code as `packages/shared`. Finally, I used webpack aliases to link all of this together:\n\n```js\nconfig.resolve.alias.shared = path.resolve(__dirname, '../shared')\nconfig.resolve.alias.site = path.resolve(__dirname)\n```\n\nThe first alias `shared` allows us to import a shared component as such:\n\n```js\nimport AppWrapper from 'shared/components/wrappers/app'\n```\n\nBut what's more, a site-specific component can be imported as such:\n\n```js\nimport NavigationButtons from 'site/components/navigation-buttons'\n```\n\nThis also works inside shared components. Which gives us a flexible way to add site-specific code to a component that is shared between freesewing.dev and freesewing.org.\n\nIt also avoids this kind of maintenance nightmare:\n\n```js\nimport Icon from '../../../../../../../components/Icon'\n```\n\nSo far, all this seems to work well with no unexpected side-effects 🤞",
"caption": "Picture by Ihsan Aditya via Pexels",
"slug": "shared-frontend-code-monorepo",
"date": "2021-07-08",
"title": "Better code sharing by bringing frontend code into our monorepo",
"linktitle": "Sharing frontend code in our monorepo",
"published_at": "2021-07-11T16:59:43.035Z",
"createdAt": "2021-07-11T16:59:40.699Z",
"updatedAt": "2021-07-23T15:04:46.863Z",
"__v": 0,
"author": "joostdecock",
"image": {
"_id": "60eb236bdb32b45d5c4d6d9e",
"name": "pexels-ihsan-aditya-1056251.jpg",
"alternativeText": "",
"caption": "",
"hash": "pexels_ihsan_aditya_1056251_41549d8d66",
"ext": ".jpg",
"mime": "image/jpeg",
"size": 329.3,
"width": 1920,
"height": 1280,
"url": "/uploads/pexels_ihsan_aditya_1056251_41549d8d66.jpg",
"formats": {
"thumbnail": {
"name": "thumbnail_pexels-ihsan-aditya-1056251.jpg",
"hash": "thumbnail_pexels_ihsan_aditya_1056251_41549d8d66",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 234,
"height": 156,
"size": 8.22,
"path": null,
"url": "/uploads/thumbnail_pexels_ihsan_aditya_1056251_41549d8d66.jpg"
},
"large": {
"name": "large_pexels-ihsan-aditya-1056251.jpg",
"hash": "large_pexels_ihsan_aditya_1056251_41549d8d66",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 1000,
"height": 667,
"size": 94.56,
"path": null,
"url": "/uploads/large_pexels_ihsan_aditya_1056251_41549d8d66.jpg"
},
"medium": {
"name": "medium_pexels-ihsan-aditya-1056251.jpg",
"hash": "medium_pexels_ihsan_aditya_1056251_41549d8d66",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 750,
"height": 500,
"size": 56.07,
"path": null,
"url": "/uploads/medium_pexels_ihsan_aditya_1056251_41549d8d66.jpg"
},
"small": {
"name": "small_pexels-ihsan-aditya-1056251.jpg",
"hash": "small_pexels_ihsan_aditya_1056251_41549d8d66",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 500,
"height": 333,
"size": 28.71,
"path": null,
"url": "/uploads/small_pexels_ihsan_aditya_1056251_41549d8d66.jpg"
}
},
"provider": "local",
"related": [
"60eb237cdb32b45d5c4d6d9f"
],
"createdAt": "2021-07-11T16:59:23.684Z",
"updatedAt": "2021-07-11T16:59:40.702Z",
"__v": 0,
"id": "60eb236bdb32b45d5c4d6d9e"
},
"site": "60ead51670e8d35a6d089fa0",
"locale": "en",
"id": "60eb237cdb32b45d5c4d6d9f",
"intro": "Up until now, our websites each live in their own repository. This makes sharing code and content somewhat more involved, and situation that we currently handle as follows:"
}

View file

@ -0,0 +1,87 @@
{
"dev": true,
"localizations": [],
"_id": "60eb2e11db32b45d5c4d6da5",
"title": "Strapi as headless CMS",
"linktitle": "Strapi as headless CMS",
"body": "As things are now, all of FreeSewing's content is contained [in our markdown repository](https://github.com/freesewing/markdown/).\n\nHistorically, that made a lot of sense:\n\n - I am perfectly happy to write markdown and put it in git\n - We need that content in a repo to we can translate it with [Crowding](https://crowdin.com)\n\nHowever, as the project grows, this approach is starting to show some real downsides:\n\n - It is scary/intimidating for new users to get familiar with git(hub) just to write a blog post or submit a showcase\n - The fact that the markdown repo is included as a submodule in our freesewing.org and freesewing.dev repositories means there's extra hoops to jump through before changes show up on the website\n - Translators have a lot of translation work in their queue with arguably little added value (like a blog post from 2 years ago).\n\nI've been thinking about this within the context of our [project 2022](/blog/project-2022) and I think we should make a distinction between two types of content:\n\n- **documentation**: This should be in git because we want a history, we want to include that little pencil icon that lets people fix errors directly on the github website, and should be translated as close as possible and as complete as possible to provide consistent info in different languages.\n- **posts**: Specifically blog posts, and to a lesser extend showcase posts. This content does not really have to live in a git repository. It is much more a write-once, never look back sort of thing.\n\nI also feel it makes little sense to translate blog posts word for word. Sure, blog posts announcing new patterns and so on would preferable be available in all languages. But I'd like to lower the bar for people to contribute to our blog, and that includes letting people write original content in languages other than English.\n\nSo, as a sort of test, I have setup [Strapi](https://strapi.io/) and it is the source for all blog posts on this developer blog.\n\nStrapi is a so-called *headless CMS* (where CMS stands for content management system). You might be familiar with Wordpress, which is a more classic CMS. A headless CMS provides similar functionality for creating and editing content. But rather than generating a website, it comes with an API you can talk to to get your content. Such an approach works great with the kind of [JAMstack](https://jamstack.org/) websites we buid.\n\nWhile it's early to draw conclusions, there's a bunch of things that I feel make this an avenue worth pursuing:\n\n- Writing and editing content is significantly simpler\n- Images can simply be uploaded, which was a pain point in the current setup\n- Keeping (large) images out of a git repository makes a lot of sense. Case in point: Our markdown repo is currently about 400MB and growing steadily. in comparison, our monorepo is less than 10% of that size.\n- All the image handling and resizing makes build times exceedingly slow. It's also somewhat frustrating that we have to do this on every build, when in reality, the images don't change\n\nThe Strapi instance I've setup lives at https://posts.freesewing.org/ \n\nI would like to configure authentication in such a way that you can either re-use your FreeSewing accounts, or your Discord account.\n\nStrapi uses the same Mongo database server as the main website, just a different database).\n\nI think this is a promising path to go down on. Anecdotal evidence: this is my 7th blog post since setting it up 😄 It's just nice to be able to dump your thoughts into a post without that having to be a big deal.\n\nUntil I've setup proper authentication, I can create an account for you if you'd like to kick the tires. As usual, the [#project-2022 channel on our Discord server](https://discord.freesewing.org/) is the place to be.\n",
"caption": "Headless. Get it? - Picture by Leah Kelley via Pexels",
"published_at": "2021-07-11T17:45:31.369Z",
"createdAt": "2021-07-11T17:44:49.116Z",
"updatedAt": "2021-07-23T15:04:36.017Z",
"__v": 0,
"author": "joostdecock",
"image": {
"_id": "60eb2d74db32b45d5c4d6da4",
"name": "pexels-leah-kelley-5852942.jpg",
"alternativeText": "",
"caption": "",
"hash": "pexels_leah_kelley_5852942_3a27bb6b4e",
"ext": ".jpg",
"mime": "image/jpeg",
"size": 113.12,
"width": 1920,
"height": 1279,
"url": "/uploads/pexels_leah_kelley_5852942_3a27bb6b4e.jpg",
"formats": {
"thumbnail": {
"name": "thumbnail_pexels-leah-kelley-5852942.jpg",
"hash": "thumbnail_pexels_leah_kelley_5852942_3a27bb6b4e",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 234,
"height": 156,
"size": 4.33,
"path": null,
"url": "/uploads/thumbnail_pexels_leah_kelley_5852942_3a27bb6b4e.jpg"
},
"large": {
"name": "large_pexels-leah-kelley-5852942.jpg",
"hash": "large_pexels_leah_kelley_5852942_3a27bb6b4e",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 1000,
"height": 666,
"size": 36.1,
"path": null,
"url": "/uploads/large_pexels_leah_kelley_5852942_3a27bb6b4e.jpg"
},
"medium": {
"name": "medium_pexels-leah-kelley-5852942.jpg",
"hash": "medium_pexels_leah_kelley_5852942_3a27bb6b4e",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 750,
"height": 500,
"size": 23.26,
"path": null,
"url": "/uploads/medium_pexels_leah_kelley_5852942_3a27bb6b4e.jpg"
},
"small": {
"name": "small_pexels-leah-kelley-5852942.jpg",
"hash": "small_pexels_leah_kelley_5852942_3a27bb6b4e",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 500,
"height": 333,
"size": 13.02,
"path": null,
"url": "/uploads/small_pexels_leah_kelley_5852942_3a27bb6b4e.jpg"
}
},
"provider": "local",
"related": [
"60eb2e11db32b45d5c4d6da5"
],
"createdAt": "2021-07-11T17:42:12.875Z",
"updatedAt": "2021-07-11T17:44:49.121Z",
"__v": 0,
"id": "60eb2d74db32b45d5c4d6da4"
},
"site": "60ead51670e8d35a6d089fa0",
"date": "2021-07-10",
"slug": "strapi-headless-cms",
"locale": "en",
"id": "60eb2e11db32b45d5c4d6da5",
"intro": "As things are now, all of FreeSewing's content is contained in our markdown repository ."
}

View file

@ -0,0 +1,87 @@
{
"dev": true,
"localizations": [],
"_id": "60eb1836db32b45d5c4d6d99",
"body": "Building a website that is supposed to do something essentially boils down to two things:\n\n - Make it work\n - Make it pretty\n\nMaking it work is a challenge of its own, but it's making things pretty that is the biggest time-sink.\n\nIt can be fun to take a deep-dive into CSS and tweak every little aspect of how things look. But you need an ocean of discipline (or perhaps a good design system) to make sure things are consistent.\n\nFor a while now, there's a new kid on the block that promises to help with taming this CSS mess: [Tailwind CSS](https://tailwindcss.com/).\n\nThere's a lot of people who love it, there's some haters too. I kicked the tires a bit and once you get used to the declarative approach, I find it to be rather pleasant to work with.\n\nSo, I am going to use Tailwind to handle the style for [project 2022](/blog/project-2022). I am hopeful that this will also lower the barrier for contributors to go ahead and make things pretty. Because things can always be prettier.",
"caption": "Pretty colors - Picture by Suleyman Seykan via Pexels",
"slug": "tailwind-css-project-2022",
"date": "2021-07-06",
"title": "Taming CSS complexity with TailwindCSS",
"linktitle": "Tailwind CSS in project 2022",
"published_at": "2021-07-11T16:11:54.384Z",
"createdAt": "2021-07-11T16:11:34.563Z",
"updatedAt": "2021-07-23T15:04:59.003Z",
"__v": 0,
"author": "joostdecock",
"image": {
"_id": "60eb1815db32b45d5c4d6d98",
"name": "pexels-suleyman-seykan-6068893.jpg",
"alternativeText": "",
"caption": "",
"hash": "pexels_suleyman_seykan_6068893_2130d0ee2c",
"ext": ".jpg",
"mime": "image/jpeg",
"size": 492.08,
"width": 1920,
"height": 1280,
"url": "/uploads/pexels_suleyman_seykan_6068893_2130d0ee2c.jpg",
"formats": {
"thumbnail": {
"name": "thumbnail_pexels-suleyman-seykan-6068893.jpg",
"hash": "thumbnail_pexels_suleyman_seykan_6068893_2130d0ee2c",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 234,
"height": 156,
"size": 14.25,
"path": null,
"url": "/uploads/thumbnail_pexels_suleyman_seykan_6068893_2130d0ee2c.jpg"
},
"large": {
"name": "large_pexels-suleyman-seykan-6068893.jpg",
"hash": "large_pexels_suleyman_seykan_6068893_2130d0ee2c",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 1000,
"height": 667,
"size": 167.88,
"path": null,
"url": "/uploads/large_pexels_suleyman_seykan_6068893_2130d0ee2c.jpg"
},
"medium": {
"name": "medium_pexels-suleyman-seykan-6068893.jpg",
"hash": "medium_pexels_suleyman_seykan_6068893_2130d0ee2c",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 750,
"height": 500,
"size": 103.55,
"path": null,
"url": "/uploads/medium_pexels_suleyman_seykan_6068893_2130d0ee2c.jpg"
},
"small": {
"name": "small_pexels-suleyman-seykan-6068893.jpg",
"hash": "small_pexels_suleyman_seykan_6068893_2130d0ee2c",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 500,
"height": 333,
"size": 53.76,
"path": null,
"url": "/uploads/small_pexels_suleyman_seykan_6068893_2130d0ee2c.jpg"
}
},
"provider": "local",
"related": [
"60eb1836db32b45d5c4d6d99"
],
"createdAt": "2021-07-11T16:11:01.674Z",
"updatedAt": "2021-07-11T16:11:34.567Z",
"__v": 0,
"id": "60eb1815db32b45d5c4d6d98"
},
"site": "60ead51670e8d35a6d089fa0",
"locale": "en",
"id": "60eb1836db32b45d5c4d6d99",
"intro": "Building a website that is supposed to do something essentially boils down to two things:"
}

View file

@ -0,0 +1,87 @@
{
"dev": true,
"localizations": [],
"_id": "60eaceb44f0f4957dbc9d64b",
"title": "Welcome to FreeSewing's development blog",
"slug": "welcome-to-our-dev-blog",
"date": "2021-07-04",
"linktitle": "Welcome to our dev blog",
"caption": "Brought to you by a lot of coffee - Photo by Kaboompics.com via Pexels",
"body": "The internet is plastered with abandoned or dormant blogs, so starting another one is perhaps madness, but hear me out:\n\n- I'm working on some new stuff, and I'd like to talk about it so people can weigh in with their opinions and feedback\n- I am often reluctant to write on the *main* FreeSewing blog about things that are way too technical for the average visitor\n- I have some wild plans for how to handle our blog content, and I need a place to kick the tires\n\nSo, without much fanfare, I am launching this blog. Here we go.",
"published_at": "2021-07-11T12:29:54.663Z",
"createdAt": "2021-07-11T10:57:56.433Z",
"updatedAt": "2021-07-23T15:05:17.024Z",
"__v": 0,
"author": "joostdecock",
"image": {
"_id": "60eace684f0f4957dbc9d64a",
"name": "pexels-kaboompics-com-6347.jpg",
"alternativeText": "",
"caption": "",
"hash": "pexels_kaboompics_com_6347_2e9a6ffd54",
"ext": ".jpg",
"mime": "image/jpeg",
"size": 210.66,
"width": 1920,
"height": 1280,
"url": "/uploads/pexels_kaboompics_com_6347_2e9a6ffd54.jpg",
"formats": {
"thumbnail": {
"name": "thumbnail_pexels-kaboompics-com-6347.jpg",
"hash": "thumbnail_pexels_kaboompics_com_6347_2e9a6ffd54",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 234,
"height": 156,
"size": 4.95,
"path": null,
"url": "/uploads/thumbnail_pexels_kaboompics_com_6347_2e9a6ffd54.jpg"
},
"large": {
"name": "large_pexels-kaboompics-com-6347.jpg",
"hash": "large_pexels_kaboompics_com_6347_2e9a6ffd54",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 1000,
"height": 667,
"size": 50.35,
"path": null,
"url": "/uploads/large_pexels_kaboompics_com_6347_2e9a6ffd54.jpg"
},
"medium": {
"name": "medium_pexels-kaboompics-com-6347.jpg",
"hash": "medium_pexels_kaboompics_com_6347_2e9a6ffd54",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 750,
"height": 500,
"size": 28.35,
"path": null,
"url": "/uploads/medium_pexels_kaboompics_com_6347_2e9a6ffd54.jpg"
},
"small": {
"name": "small_pexels-kaboompics-com-6347.jpg",
"hash": "small_pexels_kaboompics_com_6347_2e9a6ffd54",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 500,
"height": 333,
"size": 14.48,
"path": null,
"url": "/uploads/small_pexels_kaboompics_com_6347_2e9a6ffd54.jpg"
}
},
"provider": "local",
"related": [
"60eaceb44f0f4957dbc9d64b"
],
"createdAt": "2021-07-11T10:56:40.376Z",
"updatedAt": "2021-07-11T10:57:56.439Z",
"__v": 0,
"id": "60eace684f0f4957dbc9d64a"
},
"site": "60ead51670e8d35a6d089fa0",
"locale": "en",
"id": "60eaceb44f0f4957dbc9d64b",
"intro": "The internet is plastered with abandoned or dormant blogs, so starting another one is perhaps madness, but hear me out:"
}

View file

@ -1,4 +0,0 @@
import { posts as en } from './strapi.blog.en.js'
export default { en }

View file

@ -32,14 +32,14 @@ const Breadcrumbs = ({ app, slug=false, title }) => {
<ul className="flex flex-row gap-2 font-bold"> <ul className="flex flex-row gap-2 font-bold">
<li> <li>
<Link href="/"> <Link href="/">
<a title="To the homepage"> <a title="To the homepage" className="text-base-content">
<Logo size={24} /> <Logo size={24} fill="currentColor" stroke={false}/>
</a> </a>
</Link> </Link>
</li> </li>
{crumbs.map(crumb => ( {crumbs.map(crumb => (
<> <>
<li>&raquo;</li> <li className="text-base-content">&raquo;</li>
<li> <li>
{crumb[2] {crumb[2]
? ( ? (
@ -49,7 +49,7 @@ const Breadcrumbs = ({ app, slug=false, title }) => {
</a> </a>
</Link> </Link>
) )
: crumb[0] : <span className="text-base-content">{crumb[0]}</span>
} }
</li> </li>
</> </>
@ -78,7 +78,7 @@ const DefaultLayout = ({ app, title=false, children=[]}) => {
<Header app={app}/> <Header app={app}/>
<div className={` <div className={`
h-1 w-full theme-gradient ${app.loading ? 'loading' : ''} h-1 w-full theme-gradient ${app.loading ? 'loading' : ''}
fixed top-0 right-0 z-20 fixed top-0 right-0 z-40
-mt-1 -mt-1
`}></div> `}></div>
<main className={` <main className={`

File diff suppressed because one or more lines are too long

View file

@ -38,7 +38,7 @@ const howActive = (slug) => {
} }
// Shared classes for links // Shared classes for links
const linkClasses = "text-lg lg:text-xl py-1 hover:cursor-pointer hover:text-secondary bg-opacity-50" const linkClasses = "text-lg text-base-content lg:text-xl py-1 hover:cursor-pointer hover:text-secondary bg-opacity-50"
// Figure out whether a page is on the path to the active page // Figure out whether a page is on the path to the active page
const isActive = (slug, active) => { const isActive = (slug, active) => {
@ -130,11 +130,14 @@ const TopLogo = ({ app }) => (
<div className={` <div className={`
flex flex-row uppercase gap-4 font-bold text-lg flex flex-row uppercase gap-4 font-bold text-lg
items-center items-center
hover:cursor-row-resize
p-2 p-2
text-base-content text-base-content
`}> `}>
<Logo size={32} theme={app.theme} /> <Link href='/'>
<a className="hover:pointer">
<Logo size={32} fill='currentColor' stroke={false}/>
</a>
</Link>
<div> <div>
<Link href='/'> <Link href='/'>
<a className={`grow ${linkClasses}`}> <a className={`grow ${linkClasses}`}>
@ -153,10 +156,8 @@ const TopTheme = ({ app }) => (
<div className={` <div className={`
flex flex-row uppercase gap-4 font-bold text-lg flex flex-row uppercase gap-4 font-bold text-lg
items-center items-center
hover:cursor-row-resize
hover:bg-base-200
p-2 p-2
text-content-base text-base-content
`}> `}>
<Icon icon='theme' className="text-secondary"/> <Icon icon='theme' className="text-secondary"/>
<div className={`grow`}> <div className={`grow`}>

View file

@ -15,6 +15,8 @@
"@tailwindcss/typography": "^0.5.0", "@tailwindcss/typography": "^0.5.0",
"daisyui": "^1.16.2", "daisyui": "^1.16.2",
"lodash.orderby": "^4.6.0", "lodash.orderby": "^4.6.0",
"react-markdown": "^7.1.1",
"react-timeago": "^6.2.1",
"rehype-highlight": "^5.0.1", "rehype-highlight": "^5.0.1",
"remark-extract-frontmatter": "^3.2.0", "remark-extract-frontmatter": "^3.2.0",
"remark-frontmatter": "^4.0.1", "remark-frontmatter": "^4.0.1",

View file

@ -82,14 +82,36 @@ export const prebuildStrapi = async(site) => {
authors[type][post.author.slug] = post.author authors[type][post.author.slug] = post.author
posts[lang][type][slug].author = post.author.slug posts[lang][type][slug].author = post.author.slug
} }
// Write to disc, one file for the index page, one file for each post
fs.writeFileSync( fs.writeFileSync(
path.resolve('..', `freesewing.${site}`, 'prebuild', `strapi.${type}.${lang}.js`), path.resolve('..', `freesewing.${site}`, 'prebuild', `strapi.${type}.${lang}.js`),
`export const posts = ${JSON.stringify(posts[lang][type], null, 2)}` `export const posts = ${JSON.stringify(Object.values(posts[lang][type]).map(post => ({
) title: post.title,
fs.writeFileSync( date: post.date,
path.resolve('..', `freesewing.${site}`, 'prebuild', `strapi.${type}.authors.js`), slug: post.slug,
`export const authors = ${JSON.stringify(authors[type], null, 2)}` author: post.author,
img: post.image.formats.large.url
})), null, 2)}`
) )
for (const [slug, post] of Object.entries(posts[lang][type])) {
fs.writeFileSync(
path.resolve('..', `freesewing.${site}`, 'prebuild', `strapi.${type}.${lang}.${slug}.json`),
JSON.stringify(post, null, 2)
)
}
// Write to disc, one file per author
for (const [name, author] of Object.entries(authors[type])) {
fs.writeFileSync(
path.resolve('..', `freesewing.${site}`, 'prebuild', `strapi.authors.${type}.${lang}.${name}.json`),
JSON.stringify({
slug: author.slug,
name: author.name,
displayname: author.displayname,
about: author.about,
img: author.picture?.formats?.medium?.url,
}, null, 2)
)
}
} }
} }

View file

@ -0,0 +1,58 @@
// We need fs and path to read from disk
import fs from 'fs'
import path from 'path'
// MDX compiler
import { compile } from '@mdx-js/mdx'
// Remark plugins we want to use
import remarkFrontmatter from 'remark-frontmatter'
import remarkGfm from 'remark-gfm'
import remarkCopyLinkedFiles from 'remark-copy-linked-files'
// Rehype plugins we want to use
import rehypeHighlight from 'rehype-highlight'
/*
* Summary: Loads strapi content from disk
*
* @param (string) language - language to load (eg: 'en')
* @param (string) site - site to load (either 'dev' or 'org')
* @param (string) type - type of content to load (either 'blog', 'showcase' or 'author')
* @param (string) slug - slug of the page (eg: 'pattern-docs-option-sampling')
*
* @link https://mdxjs.com/guides/mdx-on-demand/
*
*/
const strapiLoader = async (language, site, type, slug) => {
const post = JSON.parse(
( await fs.promises.readFile(
path.resolve('..', `freesewing.${site}`, 'prebuild', `strapi.${type}.${language}.${slug}.json`),
'utf-8'
))
)
const author = JSON.parse(
( await fs.promises.readFile(
path.resolve('..', `freesewing.${site}`, 'prebuild', `strapi.authors.${type}.${language}.${post.author}.json`),
'utf-8'
))
)
// Compile MDX
post.mdx = String(
await compile(post.body, {
outputFormat: 'function-body',
remarkPlugins: [
remarkFrontmatter,
remarkGfm,
],
rehypePlugins: [
rehypeHighlight
],
})
)
return { post, author }
}
export default strapiLoader

View file

@ -15,9 +15,9 @@ module.exports = {
'accent-focus': colors.pink['400'], 'accent-focus': colors.pink['400'],
'accent-content': colors.pink['50'], 'accent-content': colors.pink['50'],
'neutral': colors.neutral['300'], 'neutral': colors.neutral['800'],
'neutral-focus': colors.neutral['50'], 'neutral-focus': colors.neutral['50'],
'neutral-content': colors.neutral['900'], 'neutral-content': colors.neutral['50'],
'base-100': colors.neutral['900'], 'base-100': colors.neutral['900'],
'base-200': colors.neutral['700'], 'base-200': colors.neutral['700'],
@ -36,10 +36,10 @@ module.exports = {
'--theme-gradient': `repeating-linear-gradient( '--theme-gradient': `repeating-linear-gradient(
-45deg, -45deg,
${colors.gray[300]}, ${colors.neutral[300]},
${colors.gray[300]} 15px, ${colors.neutral[300]} 10px,
transparent 15px, ${colors.neutral[800]} 10px,
transparent 25px ${colors.neutral[800]} 20px
)`, )`,
'--code-background-color': '#111', '--code-background-color': '#111',

View file

@ -7,8 +7,8 @@ module.exports = {
'primary-focus': colors.lime['600'], 'primary-focus': colors.lime['600'],
'primary-content': colors.lime['50'], 'primary-content': colors.lime['50'],
'secondary': colors.lime['700'], 'secondary': colors.lime['400'],
'secondary-focus': colors.lime['600'], 'secondary-focus': colors.lime['300'],
'secondary-content': bg, 'secondary-content': bg,
'accent': colors.lime['700'], 'accent': colors.lime['700'],

View file

@ -9,18 +9,18 @@ module.exports = {
'base-content': colors.neutral['700'], 'base-content': colors.neutral['700'],
'primary': colors.sky['500'], 'primary': colors.sky['500'],
'primary-focus': colors.sky['600'], 'primary-focus': colors.sky['400'],
'primary-content': colors.sky['100'], 'primary-content': colors.sky['50'],
'secondary': colors.violet['500'], 'secondary': colors.violet['400'],
'secondary-focus': colors.violet['400'], 'secondary-focus': colors.violet['600'],
'secondary-content': colors.violet['300'], 'secondary-content': colors.violet['50'],
'accent': colors.emerald['500'], 'accent': colors.fuchsia['500'],
'accent-focus': colors.emerald['400'], 'accent-focus': colors.fuchsia['400'],
'accent-content': colors.neutral['900'], 'accent-content': colors.neutral['50'],
'neutral': colors.neutral['800'], 'neutral': colors.neutral['800'],
'neutral-focus': colors.neutral['700'], 'neutral-focus': colors.neutral['600'],
'neutral-content': colors.neutral['200'], 'neutral-content': colors.neutral['200'],
'info': colors.pink['400'], 'info': colors.pink['400'],
@ -41,9 +41,7 @@ module.exports = {
${colors.blue[500]} 80px, ${colors.blue[500]} 80px,
${colors.blue[500]} 100px, ${colors.blue[500]} 100px,
${colors.violet[500]} 100px, ${colors.violet[500]} 100px,
${colors.violet[500]} 120px, ${colors.violet[500]} 120px
transparent 120px,
transparent 125px
)`, )`,
'--code-background-color': colors.neutral['100'], '--code-background-color': colors.neutral['100'],

View file

@ -133,10 +133,10 @@ module.exports = {
*/ */
'--theme-gradient': `repeating-linear-gradient( '--theme-gradient': `repeating-linear-gradient(
-45deg, -45deg,
${colors.gray[900]}, ${colors.neutral[900]} 0,
${colors.gray[900]} 15px, ${colors.neutral[900]} 10px,
transparent 15px, ${colors.neutral[50]} 10px,
transparent 25px ${colors.neutral[50]} 20px
)`, )`,
/* CODE HIGHLIGHTING COLORS /* CODE HIGHLIGHTING COLORS

View file

@ -1,27 +1,31 @@
const colors = require('tailwindcss/colors') const colors = require('tailwindcss/colors')
const blue = '#77cbf9'
const pink = '#ecadb9'
const white = '#ffffff'
module.exports = { module.exports = {
'fontFamily': '-apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif', 'fontFamily': '-apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif',
'base-100': colors.neutral['50'], 'base-100': white,
'base-200': colors.neutral['200'], 'base-200': colors.neutral['200'],
'base-300': colors.neutral['400'], 'base-300': colors.neutral['400'],
'base-content': colors.neutral['700'], 'base-content': colors.neutral['700'],
'primary': colors.sky['500'], 'primary': pink,
'primary-focus': colors.sky['600'], 'primary-focus': blue,
'primary-content': colors.sky['100'], 'primary-content': colors.neutral['900'],
'secondary': colors.violet['500'], 'secondary': blue,
'secondary-focus': colors.violet['400'], 'secondary-focus': pink,
'secondary-content': colors.violet['300'], 'secondary-content': colors.neutral['900'],
'accent': colors.emerald['500'], 'accent': colors.neutral['700'],
'accent-focus': colors.emerald['400'], 'accent-focus': colors.neutral['900'],
'accent-content': colors.neutral['900'], 'accent-content': blue,
'neutral': colors.neutral['800'], 'neutral': colors.neutral['700'],
'neutral-focus': colors.neutral['700'], 'neutral-focus': colors.neutral['900'],
'neutral-content': colors.neutral['200'], 'neutral-content': pink,
'info': colors.pink['400'], 'info': colors.pink['400'],
'success': colors.green['600'], 'success': colors.green['600'],
@ -39,9 +43,7 @@ module.exports = {
#ecadb9 60px, #ecadb9 60px,
#ecadb9 80px, #ecadb9 80px,
#77cbf9 80px, #77cbf9 80px,
#77cbf9 100px, #77cbf9 100px
transparent 100px,
transparent 105px
)`, )`,
'--code-background-color': colors.neutral['100'], '--code-background-color': colors.neutral['100'],