├── .gitignore ├── .glitch-assets ├── README.md ├── bun.lock ├── components ├── avatar.js ├── blocks.jsx ├── craft.js ├── icon.js ├── layout.js └── meta.js ├── contentlayer.config.js ├── jsconfig.json ├── lib ├── bookmarks.js ├── components.js ├── fonts.css ├── rss.mjs ├── theme.js └── util.js ├── next.config.mjs ├── package.json ├── pages ├── 404.js ├── [sheet].js ├── _app.js ├── _document.js ├── api │ └── card.js ├── bookmarks │ └── [slug].js └── index.js ├── prettier.config.js ├── public └── fonts │ ├── Whyte-Bold.otf │ ├── Whyte-Book.otf │ └── WhyteInktrap-Bold.otf ├── sheets ├── 2018-11-03_trans_rights.mdx ├── 2018-11-04_macbook_pro.mdx ├── 2019-08-11_personal_site.mdx ├── 2019-08-12_gun_funded.mdx ├── 2019-08-14.mdx ├── 2019-08-15.mdx ├── 2019-08-15_hack_penn_site.mdx ├── 2019-08-16_icons.mdx ├── 2019-08-20.mdx ├── 2019-08-20_hack_club_first_meeting.mdx ├── 2019-08-20_hack_club_site.mdx ├── 2019-08-22_learning_web_design.mdx ├── 2019-08-22_pennsylvania.mdx ├── 2019-08-23_the_man.mdx ├── 2019-09-05_publishing_notebook_in_mdx_via_ipad.mdx ├── 2019-09-05_windy_city_hacks_newsletters.mdx ├── 2019-09-06_explaining_next_mdx_theme_ui.mdx ├── 2019-09-06_keeping_a_notebook.mdx ├── 2019-09-06_making_a_hackathon_site.mdx ├── 2019-09-06_my_websites_look_the_same.mdx ├── 2019-09-10_why_i_care_about_apple.mdx ├── 2019-09-12_ipad_coding_with_glitch.mdx ├── 2019-09-23_digital_handwriting.mdx ├── 2019-09-24_ipados_release.mdx ├── 2019-09-24_lachlan_ultra.mdx ├── 2019-09-24_learning_ios_development.mdx ├── 2019-09-24_notes_on_college.mdx ├── 2019-09-30_music_roundup_sep_2019.mdx ├── 2019-10-30_dash_plus_system.mdx ├── 2019-10-31_music_roundup_october_2019.mdx ├── 2019-11-04_just_my_own_little_website.mdx ├── 2019-11-05_custom_social_card_services.mdx ├── 2019-11-05_real_web_development_on_ipad.mdx ├── 2019-11-14_music_roundup_mid_nov_2019.mdx ├── 2019-11-30_music_roundup_late_november_2019.mdx ├── 2019-12-02_5_years_since_launch.mdx ├── 2019-12-02_how_i_take_notes_for_college.mdx ├── 2019-12-02_switching_from_bear_to_apple_notes.mdx ├── 2019-12-11_shareable_profiles_for_gun_funded.mdx ├── 2019-12-13_music_roundup_mid_december_2019.mdx ├── 2020-01-16_music_roundup_mid_january_2020.mdx ├── 2020-01-19_how_to_start_your_first_hackathon.mdx ├── 2020-01-26_tracking_twitter_opens_with_shortcuts.mdx ├── 2020-01-28_burned_out_on_product_hunt.mdx ├── 2020-01-29_music_roundup_late_january_2020.mdx ├── 2020-02-09_how_to_survive_high_school.mdx ├── 2020-02-09_music_roundup_early_february_2020.mdx ├── 2020-02-09_my_creative_process.mdx ├── 2020-02-27_designing_with_color.mdx ├── 2020-03-20_building_predict_covid_with_redwood.mdx ├── 2020-03-28_music_roundup_late_march_2020.mdx ├── 2020-03-29_going_back_to_normal.mdx ├── 2020-04-05_music_roundup_early_april_2020.mdx ├── 2020-04-13_fonts_on_ipad.mdx ├── 2020-04-13_music_roundup_mid_april_2020.mdx ├── 2020-04-21_the_story_of_covid.mdx ├── 2020-05-01_covid_art.mdx ├── 2020-05-19_music_roundup_mid_may_2020.mdx ├── 2020-06-20_music_roundup_mid_june_2020.mdx ├── 2020-07-07_leave_of_absence.mdx ├── 2020-07-19_music_roundup_mid_july_2020.mdx ├── 2020-07-30_how_scrapbook_works.mdx ├── 2020-08-01_online_hackathons_usually_suck.mdx ├── 2020-08-08_the_problem_with_online_events.mdx ├── 2020-09-14_music_roundup_september_2020.mdx ├── 2020-10-17_music_roundup_october_2020.mdx ├── 2020-10-17_why_i_choose_next_over_gatsby.mdx ├── 2020-11-12_i_quit_hack_club.mdx ├── 2020-11-17_music_roundup_november_2020.mdx ├── 2020-12-09_take_advantage_of_intern_impostor_syndrome.mdx ├── 2020-12-16_music_roundup_december_2020.mdx ├── 2020-12-25_production_value_changes_the_product.mdx ├── 2020-12-27_a_merry_2020_christmas.mdx ├── 2021-01-13_music_roundup_january_2021.mdx ├── 2021-01-13_prioritizing_awareness_enables_my_focus.mdx ├── 2021-01-14_ipados_14_is_the_best_digital_handwriting_yet.mdx ├── 2021-01-15_airpods_max_are_priced_perfectly.mdx ├── 2021-01-21_design_with_whitespace_and_confidence.mdx ├── 2021-01-21_fonts_on_ipad_2021_edition.mdx ├── 2021-01-22_apple_fitness_is_my_new_favorite_habit.mdx ├── 2021-01-27_design_and_content_go_hand-in-hand.mdx ├── 2021-01-28_my_8_software_subscriptions.mdx ├── 2021-01-30_you_cant_be_in_complete_awe.mdx ├── 2021-02-01_nextjs_analytics_are_still_a_handy_mvp.mdx ├── 2021-02-27_music_roundup_february_2021.mdx ├── 2021-03-02_automate_setting_up_new_macs.mdx ├── 2021-03-18_visualize_apple_watch_activity_in_react.mdx ├── 2021-03-19_new_gear_im_enjoying_march_2021.mdx ├── 2021-04-18_music_roundup_april_2021.mdx ├── 2021-05-20_music_roundup_may_2021.mdx ├── 2021-06-21_how_to_meet_twitter_friends_irl.mdx ├── 2021-06-25_music_roundup_june_2021.mdx ├── 2021-07-20_music_roundup_july_2021.mdx ├── 2021-07-24_the_apple_watch_needs_decisive_editing.mdx ├── 2021-08-23_priotize_optimization_axes_on_the_web.mdx ├── 2021-09-06_the_magsafe_battery_pack_is_mediocre.mdx ├── 2021-10-16_music_roundup_october_2021.mdx ├── 2021-11-20_music_roundup_november_2021.mdx ├── 2021-12-18_music_roundup_december_2021.mdx ├── 2022-01-27_migrate_reeder_articles_to_matter_with_shortcuts.mdx ├── 2022-01-30_music_roundup_january_2022.mdx ├── 2022-02-01_evolve_your_mdx_workflow_with_a_headless_cms.mdx ├── 2022-02-05_3_tips_from_resizing_images_with_macos_shortcuts.mdx ├── 2022-08-18_set_up_rss_with_contentlayer_and_mdx.mdx ├── 2022-10-17_say_something_real.mdx ├── 2022-11-18_link_your_domain_to_mastodon_with_nextjs.mdx ├── 2022-11-30_animating_an_icon_with_tailwind_css.mdx ├── 2023-01-19_use_mdx_as_nextjs_13_server_component.mdx ├── 2023-01-23_generate_nextjs_image_blur_placeholders.mdx ├── 2023-01-25_2023_css_wishlist.mdx ├── 2023-01-30_im_not_an_environmentalist.mdx ├── 2023-02-27_notes_on_data_center_water_usage.mdx ├── 2023-09-28_good_products_stand_alone.mdx ├── 2024-03-06_long_on_spatial_computing.mdx ├── 2024-03-08_open_source_your_schoolwork_too.mdx ├── 2024-04-11_your_tools_shape_your_focus.mdx ├── 2024-04-17_10_features_nextjs_15_could_launch.mdx ├── 2024-08-20_daylight_feedback_summer_2024.mdx ├── 2024-09-19_visionos_2_invests_in_core_technologies.mdx ├── 2024-10-10_promote_national_progress_like_iphones.mdx ├── 2025-02-19_apple_vision_glasses_will_be_irresistible.mdx ├── concerts.mdx ├── hardware.mdx ├── ipad-apps.mdx └── material_goods.mdx ├── vercel.json └── watch.json /.gitignore: -------------------------------------------------------------------------------- 1 | .next 2 | node_modules 3 | .vercel 4 | .DS_Store 5 | .contentlayer 6 | -------------------------------------------------------------------------------- /.glitch-assets: -------------------------------------------------------------------------------- 1 | {"name":"summertime-f61edb30-98c7-41ea-b47f-dc96aa8ca0c5.png","date":"2019-08-20T21:48:30.486Z","url":"https://cdn.glitch.com/a9442788-bc48-44ab-bedd-832d738c93dd%2Fsummertime-f61edb30-98c7-41ea-b47f-dc96aa8ca0c5.png","type":"image/png","size":174064,"imageWidth":1070,"imageHeight":1408,"thumbnail":"https://cdn.glitch.com/a9442788-bc48-44ab-bedd-832d738c93dd%2Fthumbnails%2Fsummertime-f61edb30-98c7-41ea-b47f-dc96aa8ca0c5.png","thumbnailWidth":251,"thumbnailHeight":330,"uuid":"4xk2Z2JK0eT3voBB"} 2 | {"name":"5476C8E7-96BD-4411-BD63-126D5CF988CC-78cfbcb2-aab9-4bf6-9e45-2c88640a7e46.png","date":"2019-08-20T21:48:31.096Z","url":"https://cdn.glitch.com/a9442788-bc48-44ab-bedd-832d738c93dd%2F5476C8E7-96BD-4411-BD63-126D5CF988CC-78cfbcb2-aab9-4bf6-9e45-2c88640a7e46.png","type":"image/png","size":258631,"imageWidth":1576,"imageHeight":751,"thumbnail":"https://cdn.glitch.com/a9442788-bc48-44ab-bedd-832d738c93dd%2Fthumbnails%2F5476C8E7-96BD-4411-BD63-126D5CF988CC-78cfbcb2-aab9-4bf6-9e45-2c88640a7e46.png","thumbnailWidth":330,"thumbnailHeight":158,"uuid":"dwJuo8iB7cS7zkSU"} 3 | {"name":"7846171E-3A7D-4926-B862-408C09879F1A-6830d2ae-972b-4a77-838a-c7e68a04660c.jpg","date":"2019-08-20T21:48:33.150Z","url":"https://cdn.glitch.com/a9442788-bc48-44ab-bedd-832d738c93dd%2F7846171E-3A7D-4926-B862-408C09879F1A-6830d2ae-972b-4a77-838a-c7e68a04660c.jpg","type":"image/jpeg","size":608176,"imageWidth":2048,"imageHeight":2428,"thumbnail":"https://cdn.glitch.com/a9442788-bc48-44ab-bedd-832d738c93dd%2Fthumbnails%2F7846171E-3A7D-4926-B862-408C09879F1A-6830d2ae-972b-4a77-838a-c7e68a04660c.jpg","thumbnailWidth":279,"thumbnailHeight":330,"uuid":"K1OOIsFF626qUsWY"} 4 | {"name":"IMG_0352.jpeg","date":"2019-08-22T04:42:52.518Z","url":"https://cdn.glitch.com/a9442788-bc48-44ab-bedd-832d738c93dd%2FIMG_0352.jpeg","type":"image/jpeg","size":7400528,"imageWidth":4032,"imageHeight":3024,"thumbnail":"https://cdn.glitch.com/a9442788-bc48-44ab-bedd-832d738c93dd%2Fthumbnails%2FIMG_0352.jpeg","thumbnailWidth":330,"thumbnailHeight":248,"uuid":"glcWPwSiKIeCjtzM"} 5 | {"uuid":"glcWPwSiKIeCjtzM","deleted":true} 6 | {"name":"IMG_0755.jpeg","date":"2019-08-22T04:43:27.982Z","url":"https://cdn.glitch.com/a9442788-bc48-44ab-bedd-832d738c93dd%2FIMG_0755.jpeg","type":"image/jpeg","size":1022786,"imageWidth":1536,"imageHeight":1152,"thumbnail":"https://cdn.glitch.com/a9442788-bc48-44ab-bedd-832d738c93dd%2Fthumbnails%2FIMG_0755.jpeg","thumbnailWidth":330,"thumbnailHeight":248,"uuid":"fpd621Ck0Z4i0BIz"} 7 | {"name":"cang10_01_2712_800p.jpg","date":"2019-09-24T19:30:04.918Z","url":"https://cdn.glitch.com/a9442788-bc48-44ab-bedd-832d738c93dd%2Fcang10_01_2712_800p.jpg","type":"image/jpeg","size":263072,"imageWidth":600,"imageHeight":800,"thumbnail":"https://cdn.glitch.com/a9442788-bc48-44ab-bedd-832d738c93dd%2Fthumbnails%2Fcang10_01_2712_800p.jpg","thumbnailWidth":248,"thumbnailHeight":330,"uuid":"0BrFrxZtXNzDycWd"} 8 | {"name":"589654A7-8E98-45F9-966F-7D2C79A5E67A.jpeg","date":"2019-09-24T23:42:11.405Z","url":"https://cdn.glitch.com/a9442788-bc48-44ab-bedd-832d738c93dd%2F589654A7-8E98-45F9-966F-7D2C79A5E67A.jpeg","type":"image/jpeg","size":271742,"imageWidth":2004,"imageHeight":1170,"thumbnail":"https://cdn.glitch.com/a9442788-bc48-44ab-bedd-832d738c93dd%2Fthumbnails%2F589654A7-8E98-45F9-966F-7D2C79A5E67A.jpeg","thumbnailWidth":330,"thumbnailHeight":193,"uuid":"fWQDY3jcYmozY8L9"} 9 | {"uuid":"fWQDY3jcYmozY8L9","deleted":true} 10 | {"name":"public-png.png","date":"2019-09-25T00:36:47.224Z","url":"https://cdn.glitch.com/a9442788-bc48-44ab-bedd-832d738c93dd%2Fpublic-png.png","type":"image/png","size":235501,"imageWidth":1536,"imageHeight":808,"thumbnail":"https://cdn.glitch.com/a9442788-bc48-44ab-bedd-832d738c93dd%2Fthumbnails%2Fpublic-png.png","thumbnailWidth":330,"thumbnailHeight":174,"uuid":"BBWGVSSx25t1ldx9"} 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # @lachlanjc/notebook 2 | 3 | Daily notebook for [@lachlanjc](https://lachlanjc.com), built with Next.js, [Contentlayer](https://www.contentlayer.dev/), [MDX](https://mdxjs.com/), & [Theme UI](https://theme-ui.com). 4 | 5 | [**notebook.lachlanjc.com**](https://notebook.lachlanjc.com) 6 | 7 | Code is MIT License, content may not be redistributed 8 | -------------------------------------------------------------------------------- /components/avatar.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Avatar as Base } from 'theme-ui' 3 | 4 | const Avatar = ({ size = 48, ...props }) => ( 5 | 12 | ) 13 | 14 | export default Avatar 15 | -------------------------------------------------------------------------------- /components/icon.js: -------------------------------------------------------------------------------- 1 | import { Box } from 'theme-ui' 2 | import Supercon from 'supercons' 3 | 4 | const Icon = ({ sx, ...props }) => ( 5 | 6 | 7 | 8 | ) 9 | 10 | export default Icon 11 | -------------------------------------------------------------------------------- /components/meta.js: -------------------------------------------------------------------------------- 1 | import Head from 'next/head' 2 | 3 | const Meta = ({ 4 | title = '@lachlanjc/notebook', 5 | name = '@lachlanjc/notebook', 6 | description = 'Lachlan Campbell’s daily blog, Notebook.', 7 | image = `https://${ 8 | process.env.NEXT_PUBLIC_VERCEL_URL ?? 'notebook.lachlanjc.com' 9 | }/api/card?title=Notebook`, 10 | }) => ( 11 | 12 | {title} 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 28 | 29 | ) 30 | 31 | export default Meta 32 | -------------------------------------------------------------------------------- /contentlayer.config.js: -------------------------------------------------------------------------------- 1 | import { defineDocumentType, makeSource } from 'contentlayer/source-files' 2 | import { hasDate, getDate, getName } from './lib/util' 3 | import remarkGfm from 'remark-gfm' 4 | import rehypeAccessibleEmojis from 'rehype-accessible-emojis' 5 | import rehypeCodeTitles from 'rehype-code-title' 6 | 7 | export const Sheet = defineDocumentType(() => ({ 8 | name: 'Sheet', 9 | filePathPattern: '*.mdx', 10 | contentType: 'mdx', 11 | fields: {}, 12 | computedFields: { 13 | slug: { 14 | type: 'string', 15 | resolve: ({ _raw: doc }) => doc.sourceFileName.replace(/\.mdx$/, ''), 16 | }, 17 | name: { 18 | type: 'string', 19 | resolve: ({ _raw: doc }) => 20 | getName(doc.sourceFileName.replace(/\.mdx$/, '')), 21 | }, 22 | date: { 23 | type: 'string', 24 | resolve: ({ _raw: { sourceFileName } }) => 25 | hasDate(sourceFileName) ? getDate(sourceFileName) : null, 26 | }, 27 | }, 28 | })) 29 | 30 | export default makeSource({ 31 | contentDirPath: 'sheets', 32 | documentTypes: [Sheet], 33 | mdx: { 34 | remarkPlugins: [remarkGfm], 35 | rehypePlugins: [rehypeAccessibleEmojis, rehypeCodeTitles], 36 | }, 37 | }) 38 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "paths": { 5 | "contentlayer/generated": [ 6 | "./.contentlayer/generated" 7 | ] 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /lib/bookmarks.js: -------------------------------------------------------------------------------- 1 | export const bookmarkPages = { 2 | albums: { id: 'mu8xXeQJC7LF4a', title: 'Best Albums' }, 3 | // articles: { id: 'yvvpIjHfKAJIlT', title: 'Recent Reads' }, 4 | shortcuts: { id: '1FqzgGgJElnwWw', title: 'Shortcuts' }, 5 | 'great-personal-sites': { 6 | id: 'YMr06bjrAwdVUk', 7 | title: 'Great Personal Websites', 8 | }, 9 | 'great-marketing-sites': { 10 | id: 'BGuGxEXXO1EwRR', 11 | title: 'Great Marketing Websites', 12 | }, 13 | // 'css-techniques': { id: 'vou8uLPLvPG2qU', title: 'CSS Techniques' }, 14 | 'good-google-fonts': { id: '5gPxL3EgCwWMMR', title: 'The Good Google Fonts' }, 15 | 'type-foundries': { id: 'GW5YB93X3b6j3Y', title: 'Nice Type Foundries' }, 16 | // 'open-source-fonts': { id: 'tvYJuotm2B052K', title: 'Open Source Fonts' }, 17 | 'icon-libraries': { id: 'E4pL5sowxPFWe1', title: 'React Icon Libraries' }, 18 | } 19 | 20 | export const cityPages = { 21 | berlin: { id: 'CiDzkGmGUvpvLx', title: 'Berlin' }, 22 | copenhagen: { id: 'O5YFRmh9TmEL7f', title: 'Copenhagen' }, 23 | london: { id: 'WPQi78ktXUAUKA', title: 'London' }, 24 | 'new-york-city': { id: 'oQNlyDVj1Uy42C', title: 'New York City' }, 25 | 'san-francisco': { id: 'dDG3j3RaclpYCa', title: 'San Francisco' }, 26 | } 27 | 28 | function processBlocks(blocks) { 29 | return blocks 30 | .map(({ id, content, type, rawProperties, style: rawStyle }) => { 31 | const properties = 32 | typeof rawProperties === 'string' && rawProperties.startsWith('{') 33 | ? JSON.parse(rawProperties) 34 | : {} 35 | if (properties.smartLinkData) { 36 | properties.smartLinkData = JSON.parse(properties.smartLinkData) 37 | } 38 | const style = 39 | typeof rawStyle === 'string' && rawStyle.startsWith('{') 40 | ? JSON.parse(rawStyle) 41 | : {} 42 | if (style.textStyle === 'pageRegular') { 43 | return null 44 | } 45 | return { 46 | id, 47 | content, 48 | type, 49 | properties, 50 | style, 51 | } 52 | }) 53 | .filter(Boolean) 54 | } 55 | 56 | export async function getDocBlocks(id) { 57 | const doc = await fetch( 58 | `https://www.craft.do/api/share/${id}?enablePagination=false`, 59 | ).then(res => res.json()) 60 | const blocks = processBlocks( 61 | doc.blocks[0].blocks.map(id => doc.blocks.find(block => block.id === id)), 62 | ) 63 | return blocks 64 | } 65 | -------------------------------------------------------------------------------- /lib/components.js: -------------------------------------------------------------------------------- 1 | /** @jsxImportSource theme-ui */ 2 | import { Text, Button, Themed } from 'theme-ui' 3 | import Prism from '@theme-ui/prism' 4 | import Link from 'next/link' 5 | import { kebabCase } from 'lodash-es' 6 | 7 | const wavy = { 8 | textDecoration: 'underline', 9 | textUnderlinePosition: 'under', 10 | WebkitTextUnderlinePosition: 'under', 11 | textDecorationStyle: 'wavy', 12 | WebkitTextDecorationStyle: 'wavy', 13 | } 14 | const headingLink = { 15 | color: 'inherit', 16 | textDecoration: 'none', 17 | } 18 | 19 | const Code = ({ children, filename, ...props }) => { 20 | if (typeof children === 'string' && !children.includes('\n')) { 21 | return {children} 22 | } 23 | return 24 | } 25 | Code.defaultProps = { className: '' } 26 | 27 | export default { 28 | pre: ({ children }) => <>{children}, 29 | code: Code, 30 | h2: props => ( 31 | 32 | 33 | {props.children} 34 | 35 | 36 | ), 37 | h3: props => ( 38 | 39 | 40 | {props.children} 41 | 42 | 43 | ), 44 | a: ({ href, ...props }) => 45 | href.startsWith('/') ? ( 46 | 47 | 48 | 49 | ) : ( 50 | 51 | ), 52 | Button, 53 | } 54 | -------------------------------------------------------------------------------- /lib/fonts.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: Whyte; 3 | font-style: normal; 4 | font-weight: 400; 5 | src: url(https://cdn.glitch.com/4d99d0f7-c364-44a5-b1b9-2c3c3f5cb333%2FWhyte-Regular-412d6af025a4cfe3d36ab0850f3b258f.woff2?v=1582525105493) 6 | format('woff2'); 7 | font-display: swap; 8 | } 9 | @font-face { 10 | font-family: Whyte; 11 | font-style: normal; 12 | font-weight: 700; 13 | src: url(https://cdn.glitch.com/4d99d0f7-c364-44a5-b1b9-2c3c3f5cb333%2FWhyte-Bold-259eea7f642aa1973e3688f57b803286.woff2?v=1582525121432) 14 | format('woff2'); 15 | font-display: swap; 16 | } 17 | 18 | @font-face { 19 | font-family: 'Whyte Arial Fallback'; 20 | src: local(Arial); 21 | size-adjust: 103%; 22 | ascent-override: 111%; 23 | descent-override: 37%; 24 | line-gap-override: normal; 25 | } 26 | 27 | @font-face { 28 | font-family: 'WhyteInktrap Arial Fallback'; 29 | src: local(Arial); 30 | size-adjust: 99%; 31 | ascent-override: 88%; 32 | descent-override: 25%; 33 | line-gap-override: normal; 34 | } 35 | 36 | @font-face { 37 | font-family: WhyteInktrap; 38 | font-style: normal; 39 | font-weight: 700; 40 | src: url(https://cdn.glitch.com/4d99d0f7-c364-44a5-b1b9-2c3c3f5cb333%2FWhyteInktrap-Bold-d5fda619e54a4948a42c0f133a4ad5ed.woff2?v=1582525136783) 41 | format('woff2'); 42 | font-display: swap; 43 | } 44 | -------------------------------------------------------------------------------- /lib/rss.mjs: -------------------------------------------------------------------------------- 1 | import RSS from 'rss' 2 | import { allSheets } from '../.contentlayer/generated/Sheet/_index.mjs' 3 | import { remark } from 'remark' 4 | import strip from 'remark-mdx-to-plain-text' 5 | import { writeFileSync } from 'fs' 6 | 7 | async function generateFeed() { 8 | const feed = new RSS({ 9 | title: '@lachlanjc/notebook', 10 | site_url: 'https://notebook.lachlanjc.com', 11 | feed_url: 'https://notebook.lachlanjc.com/feed.xml', 12 | image_url: 'https://github.com/lachlanjc.png', 13 | language: 'en_US', 14 | }) 15 | 16 | const capitalLetter = /^[A-Z]/ 17 | 18 | await Promise.all( 19 | allSheets 20 | .filter(sheet => sheet.date != null) 21 | .map(async sheet => { 22 | feed.item({ 23 | title: sheet.name, 24 | url: `https://notebook.lachlanjc.com/${sheet.slug}`, 25 | guid: sheet.slug, 26 | date: sheet.date, 27 | description: await remark() 28 | .use(strip) 29 | .process( 30 | sheet.body.raw 31 | .split('\n\n') 32 | .filter(text => text.match(capitalLetter))?.[0], 33 | ) 34 | .then(file => String(file).trim()), 35 | }) 36 | }), 37 | ) 38 | 39 | writeFileSync('./.next/static/feed.xml', feed.xml({ indent: true })) 40 | console.log(`Wrote ${allSheets.filter(sheet => sheet.date != null).length} items to RSS feed`) 41 | } 42 | 43 | console.log('[postbuild] Generating RSS feed') 44 | generateFeed() 45 | -------------------------------------------------------------------------------- /lib/util.js: -------------------------------------------------------------------------------- 1 | import title from 'title' 2 | import { isEmpty, startCase } from 'lodash-es' 3 | 4 | export const getName = path => { 5 | let name = startCase( 6 | path 7 | .replace(/(\d{4}-\d{2}-\d{2})/, '') 8 | .replace('/bookmarks/', '') 9 | .replace('/', '') 10 | .replace('nextjs', 'Next.js') 11 | .replace('priotize', 'Prioritize'), 12 | ) 13 | name = title(name, { 14 | special: [ 15 | 'iPhone', 16 | 'iPhones', 17 | 'iPad', 18 | 'MacBook', 19 | 'iOS', 20 | 'iPadOS', 21 | 'macOS', 22 | 'visionOS', 23 | 'AirPods', 24 | 'HomePod', 25 | 'Plus', 26 | 'MVP', 27 | 'MDX', 28 | 'UI', 29 | 'COVID', 30 | 'IRL', 31 | 'CMS', 32 | 'RSS', 33 | ], 34 | }) 35 | .replace(' and ', ' & ') 36 | .replace(/^Im /, 'I’m ') 37 | .replace(' Im ', ' I’m ') 38 | .replace(' Cant', ' Can’t') 39 | .replace('Theyre', 'They’re') 40 | .replace('Apple Fitness', 'Apple Fitness+') 41 | if (hasDate(path) && name === '') { 42 | name = formatDate(new Date(getDate(path))) 43 | } 44 | return name 45 | } 46 | 47 | export const hasDate = path => 48 | !isEmpty(path.toString().match(/\d{4}-\d{2}-\d{2}/)) 49 | 50 | export const getDate = path => { 51 | const match = path.match(/(\d{4}-\d{2}-\d{2})/) 52 | return match ? match[0] : '' 53 | } 54 | 55 | const dateFormatOptions = { 56 | year: 'numeric', 57 | month: 'long', 58 | day: 'numeric', 59 | timeZone: 'UTC', 60 | } 61 | export const formatDate = date => 62 | new Intl.DateTimeFormat('en-US', dateFormatOptions).format(date) 63 | 64 | export const getDescription = path => { 65 | if (path === '/') { 66 | return 'Lachlan Campbell’s personal blog, Notebook, with posts about whatever they want.' 67 | } 68 | let date = '' 69 | if (hasDate(path)) { 70 | date = new Date(getDate(path)) 71 | date = ` on ${formatDate(date)}` 72 | } 73 | return `Post by Lachlan Campbell${date} on their personal Notebook blog.` 74 | } 75 | 76 | export const getImage = path => { 77 | const host = `https://${ 78 | process.env.NEXT_PUBLIC_VERCEL_URL ?? 'notebook.lachlanjc.com' 79 | }` 80 | if (path === '/') return `${host}/api/card?title=Notebook&fontSize=400px` 81 | let name = getName(path.toString()) 82 | let caption = '' 83 | let params = '' 84 | let theme = 'light' 85 | if (hasDate(path)) { 86 | let date = getDate(path) 87 | if (path.replace(/\//g, '') !== date) { 88 | caption = formatDate(new Date(date)) 89 | } 90 | if (name.length > 30) { 91 | params += '&fontSize=225px' 92 | } 93 | } else { 94 | theme = 'dark' 95 | params += '&fontSize=275px' 96 | } 97 | name = encodeURIComponent(name) 98 | caption = encodeURIComponent(caption) 99 | return `${host}/api/card?title=${name}&theme=${theme}${params}&caption=${caption}` 100 | } 101 | -------------------------------------------------------------------------------- /next.config.mjs: -------------------------------------------------------------------------------- 1 | import { withContentlayer } from 'next-contentlayer' 2 | 3 | /** 4 | * @type {import('next').NextConfig} 5 | */ 6 | const config = withContentlayer({ 7 | swcMinify: true, 8 | compiler: { 9 | emotion: true, 10 | }, 11 | pageExtensions: ['js', 'ts', 'tsx', 'mdx'], 12 | images: { 13 | domains: ['cdn.glitch.com', 'www.icloud.com'], 14 | }, 15 | async rewrites() { 16 | return [ 17 | { source: '/shortcuts', destination: '/bookmarks/shortcuts' }, 18 | { source: '/feed.xml', destination: '/_next/static/feed.xml' }, 19 | ] 20 | }, 21 | async redirects() { 22 | return [ 23 | { source: '/ipad-apps', destination: '/ipad_apps', permanent: true }, 24 | ] 25 | }, 26 | }) 27 | 28 | export default config 29 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@lachlanjc/notebook", 3 | "version": "2.0.0", 4 | "main": "index.js", 5 | "scripts": { 6 | "dev": "next dev", 7 | "start": "next start", 8 | "build": "next build", 9 | "postbuild": "node --experimental-json-modules ./lib/rss.mjs", 10 | "contentlayer": "contentlayer build" 11 | }, 12 | "author": "Lachlan Campbell ", 13 | "license": "MIT", 14 | "private": true, 15 | "dependencies": { 16 | "@emotion/react": "^11.10.5", 17 | "@mdx-js/react": "1", 18 | "@theme-ui/preset-base": "^0.14.7", 19 | "@theme-ui/prism": "^0.14.7", 20 | "@vercel/og": "^0.0.20", 21 | "contentlayer": "^0.2.9", 22 | "lodash-es": "^4.17.21", 23 | "next": "^12.3.4", 24 | "next-contentlayer": "^0.2.9", 25 | "react": "^18.2.0", 26 | "react-dom": "^18.2.0", 27 | "react-figma-embed": "^1.0.1", 28 | "react-music-embed": "^1.0.1", 29 | "react-player": "^2.11.0", 30 | "react-song-embed": "^1.0.0", 31 | "rehype-accessible-emojis": "^0.3.2", 32 | "rehype-code-title": "^1.0.0", 33 | "remark": "^14.0.2", 34 | "remark-gfm": "^3.0.1", 35 | "remark-mdx-to-plain-text": "^3.0.0", 36 | "rss": "^1.2.2", 37 | "supercons": "^0.0.1", 38 | "theme-ui": "^0.14.7", 39 | "title": "^3.5.3" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /pages/404.js: -------------------------------------------------------------------------------- 1 | import Layout from '../components/layout' 2 | import { Themed, Button } from 'theme-ui' 3 | import Embed from 'react-song-embed' 4 | import Link from 'next/link' 5 | 6 | export default function FourOhFour() { 7 | return ( 8 | 9 | 404 10 | 11 | 12 | 15 | 16 | 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /pages/[sheet].js: -------------------------------------------------------------------------------- 1 | import { BaseStyles } from 'theme-ui' 2 | import { useMDXComponent } from 'next-contentlayer/hooks' 3 | import components from '../lib/components' 4 | import Layout from '../components/layout' 5 | import { allSheets } from 'contentlayer/generated' 6 | // import type { Sheet } from 'contentlayer/generated'; 7 | 8 | export default function Sheet({ sheet }) { 9 | const Content = useMDXComponent(sheet.body.code) 10 | 11 | return ( 12 | 13 | 14 | 15 | 16 | 17 | ) 18 | } 19 | 20 | export async function getStaticPaths() { 21 | return { 22 | paths: allSheets.map(({ slug }) => ({ params: { sheet: slug } })), 23 | fallback: false, 24 | } 25 | } 26 | 27 | export async function getStaticProps({ params }) { 28 | const sheet = allSheets.find(sheet => sheet.slug === params?.sheet) 29 | return { props: { sheet } } 30 | } 31 | -------------------------------------------------------------------------------- /pages/_app.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | 3 | import theme from '../lib/theme' 4 | import components from '../lib/components' 5 | import { ThemeProvider } from 'theme-ui' 6 | import '../lib/fonts.css' 7 | 8 | const App = ({ Component, pageProps }) => { 9 | return ( 10 | 11 | 12 | 13 | ) 14 | } 15 | 16 | export default App 17 | -------------------------------------------------------------------------------- /pages/_document.js: -------------------------------------------------------------------------------- 1 | import { Html, Head, Main, NextScript } from 'next/document' 2 | import { InitializeColorMode } from 'theme-ui' 3 | 4 | export default function Document() { 5 | return ( 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | ) 15 | } 16 | -------------------------------------------------------------------------------- /pages/bookmarks/[slug].js: -------------------------------------------------------------------------------- 1 | import { Themed } from 'theme-ui' 2 | import Layout from '../../components/layout' 3 | import CraftBlocks from '../../components/craft' 4 | import { bookmarkPages, cityPages, getDocBlocks } from '../../lib/bookmarks' 5 | 6 | export default function Bookmarks({ slug, title, blocks }) { 7 | return ( 8 | 9 | {title} 10 | 11 | 12 | ) 13 | } 14 | 15 | export async function getStaticPaths() { 16 | return { 17 | paths: Object.keys(bookmarkPages) 18 | .concat(Object.keys(cityPages)) 19 | .map(slug => ({ 20 | params: { slug }, 21 | })), 22 | fallback: false, 23 | } 24 | } 25 | 26 | export async function getStaticProps({ params }) { 27 | const { slug } = params 28 | const { title, id } = bookmarkPages[slug] ?? cityPages[slug] 29 | const blocks = await getDocBlocks(id) 30 | return { props: { slug, title, blocks }, revalidate: 20 } 31 | } 32 | -------------------------------------------------------------------------------- /pages/index.js: -------------------------------------------------------------------------------- 1 | /** @jsxImportSource theme-ui */ 2 | import Link from 'next/link' 3 | import Layout from '../components/layout' 4 | import { pick, isEmpty, orderBy } from 'lodash-es' 5 | import { Themed, Paragraph, Link as A, Heading, Divider } from 'theme-ui' 6 | import { allSheets } from 'contentlayer/generated' 7 | import { bookmarkPages, cityPages } from 'lib/bookmarks' 8 | 9 | export default function IndexPage({ sheets, now }) { 10 | return ( 11 | 12 |
13 | Notebook 14 | 15 | (where @lachlanjc publishes 16 | whatever they want) 17 | 18 |
19 | 20 |
    27 | {sheets.map(({ name, date, slug }) => ( 28 |
  1. 36 | 37 | 54 | {name} 55 | {!isEmpty(date) && ( 56 | 66 | {date} 67 | 68 | )} 69 | 70 | 71 |
  2. 72 | ))} 73 |
74 | 119 | 120 | ) 121 | } 122 | 123 | export const getStaticProps = () => { 124 | const sheets = orderBy( 125 | allSheets 126 | .concat([ 127 | { slug: 'bookmarks/albums', name: 'Favorite Albums' }, 128 | { slug: 'shortcuts', name: 'Shortcuts' }, 129 | ]) 130 | .map(sheet => pick(sheet, ['slug', 'name', 'date'])), 131 | ['date', 'name'], 132 | ['desc', 'desc'], 133 | ) 134 | 135 | return { props: { sheets } } 136 | } 137 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | singleQuote: true, 3 | trailingComma: 'all', 4 | printWidth: 80, 5 | semi: false, 6 | arrowParens: 'avoid' 7 | } 8 | -------------------------------------------------------------------------------- /public/fonts/Whyte-Bold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lachlanjc/notebook/3c1ed944976f211d655cb6d314d57abde2f1818b/public/fonts/Whyte-Bold.otf -------------------------------------------------------------------------------- /public/fonts/Whyte-Book.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lachlanjc/notebook/3c1ed944976f211d655cb6d314d57abde2f1818b/public/fonts/Whyte-Book.otf -------------------------------------------------------------------------------- /public/fonts/WhyteInktrap-Bold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lachlanjc/notebook/3c1ed944976f211d655cb6d314d57abde2f1818b/public/fonts/WhyteInktrap-Bold.otf -------------------------------------------------------------------------------- /sheets/2018-11-03_trans_rights.mdx: -------------------------------------------------------------------------------- 1 | # Trans Rights Are Human Rights 2 | 3 | ![Demonstration for trans rights](https://d1fmxjrxw87eps.cloudfront.net/2020-04-20T15:15:08-04:00.jpeg) 4 | 5 | For those of you who don’t know, I’m non-binary. 6 | 7 | Recently, the Trump administration attempted to begin legally redefining transgender out of existence. There are well over 1.5 million trans people in America, & many more around the world. Every day, our rights, our voices, & our bodies are persecuted. 8 | 9 | Last week I helped organize a rally for trans solidarity at my trans-hostile Congressman Glenn Thompson’s office, where nearly fifty incredible community members came to speak out, to sing, to read, to remember, to protest. Yesterday we held another rally in downtown State College, where again dozens gathered to remind our community: we exist, we are here, & our rights are important. 10 | 11 | This era will be remembered in social history for the ongoing gender revolution. These years are a turning point, where societally we marked a distinct shift in understanding & inclusivity. Revolutions don’t just happen, though—they happen through the tireless, unending work of the oppressed, amplified by allies. Our rights are directly under attack from the Trump administration right now, our lives are vulnerable (especially those of trans women of color, who are silently murdered at alarming rates). Every day, oppression, discrimination, bullying, violence work against the trans community. 12 | 13 | To my cis friends: if you’ve never lifted a finger to support trans people & protect trans rights, it’s past time to start. You don’t get a pass just because you’re gay. Now more than ever, we need to stand up, stand together, fight back, & fight for the inclusive future we want & we deserve. To our wonderful allies, thank you, let’s keep going. Embody it daily by supporting trans people; vote for politicians who do the same. Shout it from the rooftops: #TransRightsAreHumanRights. 14 | -------------------------------------------------------------------------------- /sheets/2018-11-04_macbook_pro.mdx: -------------------------------------------------------------------------------- 1 | # MacBook Pro (Retina, 13-inch, Late 2012) 2 | 3 | As I say goodbye to this MacBook Pro, erasing the disk, peeling off the stickers, sanitizing the enclosure, a few disjointed reflections. 4 | 5 | The majority of the work of my career I did on this computer. Purchased summer 2013, 12-year-old Lachlan used it just as 17-year-old Lachlan used it. I learned most of what I know about coding on this computer—a 2006 white plastic iMac taught me my first HTML, but every app since from [Noodles](https://getnoodl.es) to [Hack Club Bank](https://hackclub.com/bank), projects from [Call to Action](https://www.usecalltoaction.com) to finally [Gasp!](https://gasp.netlify.com) flowed through this machine. 6 | 7 | I could have done the same work using tons of other devices; there’s nothing inherently special about this MacBook Pro. But it captures a period of deep empowerment in my life, where I learned the skills to build myself a platform, a voice in the world. If I have an idea now, I can create something, I can put it out, & there are people that listen. (Still getting used to that last part.) 8 | 9 | Building tools to empower people, especially young people, especially beginning people, especially underrepresented people, is one of the most powerful things you can do. I hope that my work using this created/creates far more value than the cost of producing it. 10 | 11 | As the final sticker is peeled off, I think of the many apps I’ve built with it, the dozens of cities it’s traveled to in my backpack, the people I’ve met through it. This might have been my first real computer, but it’s just the first bit of my work. Farewell, & onward. 12 | -------------------------------------------------------------------------------- /sheets/2019-08-11_personal_site.mdx: -------------------------------------------------------------------------------- 1 | # Personal site 2 | 3 | - [x] Fix theme global styling 4 | - [ ] Make hero components for projects 5 | - [ ] Add projects 6 | - [ ] Design social card 7 | - [ ] Add social tags 8 | - [ ] Add rainbow theme 9 | - [ ] Deploy to GitHub 10 | -------------------------------------------------------------------------------- /sheets/2019-08-12_gun_funded.mdx: -------------------------------------------------------------------------------- 1 | # Gun Funded 2 | 3 | [**gunfunded.now.sh**](https://gunfunded.now.sh) 4 | 5 | - [ ] Make SEO pages 6 | - [x] Top Senators by gun funding 7 | - [x] Top Representatives by gun funding 8 | - [ ] 116th Congress by gun funding 9 | - [x] Add dark theme 10 | - [ ] Add filters to Grouping 11 | - [ ] Add [social tags](https://github.com/zeit/next-site/blob/master/components/social-meta.js) to every page 12 | - [x] Write Head wrapper component 13 | - [ ] Add great sharing 14 | - [ ] Generate social cards 15 | - [capture-website](https://github.com/sindresorhus/capture-website) 16 | - Inspiration from [NomadList](https://levels.io/phantomjs-social-media-share-pictures/) 17 | - [ ] Sharing UI 18 | - [reakit Dialog](https://reakit.io/docs/dialog/) 19 | - States 20 | - [x] Make state pages 21 | - [ ] Add colors to /states graphic 22 | - [ ] Calculate totals on states endpoint 23 | - [ ] Add quick filtering on /states 24 | - [`@theme-ui/editor` Combobox](https://github.com/system-ui/theme-ui/blob/master/packages/editor/src/Combobox.js) 25 | 26 | ## Data 27 | 28 | - [x] Fix processing bugs 29 | - [ ] Fix Fred Keller data 30 | - [ ] Add NRA independent data 31 | - [ ] Adjust rank 32 | 33 | ## Swag (cancelled) 34 | 35 | - [ ] Design stickers 36 | - [ ] Plan swag economics 37 | - [ ] Figure out order flow 38 | 39 | ## Launch 40 | 41 | - [ ] Buy domain 42 | -------------------------------------------------------------------------------- /sheets/2019-08-14.mdx: -------------------------------------------------------------------------------- 1 | # Wednesday, August 14, 2019 2 | 3 | - Built [Hack Pennsylvania recap site](https://hackpenn.com) 4 | - Made this entirely on Glitch, which was a new experience for a full JS site. Great experience now that I've got the `watch.json` files sorted out. 5 | - Organized my Glitch profile, made some Collections for Hack Happy Valley, Hack Penn, my Hack Club, etc. I love Glitch. 6 | - Got my Ruined by Design zine 7 | 8 | ## Theme UI 9 | 10 | After making the Hack Penn recap site, I feel like __I finally understand Theme UI & MDX now__. 11 | It's an incredible DX, where I don't feel like I'm writing the same boilerplate code 12 | over & over like I do with styled-components/DS/my previous stack. You can't go back. 13 | 14 | I _love_ how with MDX, your content is separated from your code. Makes it so much more approachable, too. 15 | 16 | One interesting thing is the `sx` object for styling props. 17 | Initially it looks like a downgrade—why not use first-class props like in DS? 18 | (Historical note: this is actually a switch _back_ to how Rebass v0 did it with inline styles! 19 | I was in 8th/9th grade when that happened.) 20 | But once you're using it, the `sx` object prop is so much better. The syntax is easier to type & format 21 | than JSX props, & mostly, it gives you an escape hatch. Need to do a weird selector or psuedoelement or 22 | make a little change to a tag inside? You don't need to graduate to a full-on styled-component at the 23 | top of the file with two lines of code in it, & do the awkward balancing act between `attrs`, 24 | `defaultProps`, & JSX props, you can just open up `sx={{ }}` & make the change right there. __It encourages 25 | a much healthier balance of components__—you can easily recognize when a component is getting unweildy in 26 | your MDX & move it over to a JS file to be resuable. 27 | 28 | Before Theme UI, I was feeling kinda done building Gatsby + DS sites. I've made like a dozen, & maintained 29 | hackclub.com, 2019.hackpenn.com, hackhappyvalley.com, statehigh.hackclub.com, & so many others. It's not a 30 | bad library, but I was getting tired of a lot of little patterns I didn't love. 31 | 32 | With MDX & Next.js/Gatsby & Theme UI, I've got something new, & __it feels distinctly better__. It's going to be 33 | awhile before this catches on, if it ever becomes at all popular, but in the meantime, like [I said about 34 | Next.js](https://twitter.com/lachlanjc/status/1160979484637618177?s=20), it makes me want to build things. 35 | That's when you know you've got a really incredible stack. 36 | -------------------------------------------------------------------------------- /sheets/2019-08-15.mdx: -------------------------------------------------------------------------------- 1 | # Thursday, August 14, 2019 2 | 3 | - [Wrote about](/2019-08-15_hack_penn_site) the [Hack Penn site](https://hackpenn.com) 4 | 5 | ## Goals before NYU 6 | 7 | - Launch Gun Funded 8 | - Read Ruined by Design 9 | -------------------------------------------------------------------------------- /sheets/2019-08-15_hack_penn_site.mdx: -------------------------------------------------------------------------------- 1 | # Hack Pennsylvania site 2 | 3 | Though the event ran last January, Hack Pennsylvania is one of the things I'm most proud of. I wanted there to be a nice recap of the event online, so I made it: [**hackpenn.com**](https://hackpenn.com) 4 | 5 | ## Behind the scenes 6 | 7 | The site is built using Next.js + MDX + Theme UI, with Rebass + Emotion. Breaking it down: 8 | 9 | - Next.js is the framework that bundles & serves the site. https://nextjs.org/ 10 | - MDX is a technology for Markdown ↔ React. It lets you use React components directly inside Markdown, & vice versa. https://mdxjs.com/ 11 | - Theme UI is a new React library for making themeable React apps. https://theme-ui.com/ 12 | - Rebass is a React component library wrapping Theme UI. It exposes components like `` for styling. https://rebassjs.org/ 13 | - Emotion is the "backend" of rendering the CSS-in-JS from Theme UI/Rebass. https://emotion.sh/ 14 | - The site is deployed on Netlify. Since the site has no dynamic content, it can be statically exported, so Netlify generates a folder of HTML & JS files when I push to GitHub it's then serving via CDN. Next.js automatically detects static vs dynamic pages & builds appropriately. 15 | 16 | So all the content of the site is written in standard Markdown, with a sprinkling of React components wrapping parts of the site like the top banner, grid of people, etc with special styling. 17 | 18 | Here’s an example of MDX: 19 | 20 | ```mdx 21 | 22 | 23 | # Hack Pennsylvania 24 | 25 | Write text here 26 | 27 | 28 | ``` 29 | 30 | Then in a JS file, define the component: 31 | 32 | ```js 33 | const Banner = props => ( 34 | 45 | ) 46 | ``` 47 | 48 | The `Box` component is coming from Rebass—it's actually just a `
` with Theme UI [magic](https://theme-ui.com/jsx-pragma) applied. Everything inside [the `sx` prop](https://theme-ui.com/sx-prop) is running via Theme UI. The `{...props}` forwards on all the other props passed to `Banner`—which include the Markdown content, via `children`. 49 | 50 | The `primary` color & the `mono` font family are coming from a global theme. You can define multiple color modes, like the dark theme, & use a React Hook on any component to get the current colors based on the color mode or control the current mode. When you use `color` & `bg` inside the `sx` prop, Theme UI automatically applies the correct color for the mode. A basic theme could look like this: 51 | 52 | ```js 53 | export default { 54 | fonts: { 55 | body: 'system-ui, sans-serif', 56 | mono: 'Menlo, monospace' 57 | }, 58 | initialColorMode: 'light', 59 | colors: { 60 | text: '#123', 61 | background: '#fff', 62 | primary: '#f03', 63 | modes: { 64 | dark: { 65 | text: '#fff', 66 | background: '#000', 67 | primary: '#0ef' 68 | } 69 | } 70 | } 71 | } 72 | ``` 73 | 74 | If you've used [Styled System](https://styled-system.com) before (also by the brilliant [@jxnblk](https://jxnblk.com)), you're familiar with the idea of themes & scales for whitespace, color, etc. Theme UI takes that to the next level by allowing props you're used to from Styled System (included in the [Hack Club Design System](https://design.hackclub.com)) like `py` & `bg` right alongside full CSS-in-JS, with full support for psuedoselectors, complex selectors, nesting, & more, via Emotion. I wrote some rambling thoughts on Theme UI & the `sx` prop [yesterday](/2019-08-14), if you're interested. _TLDR, Theme UI is brilliant._ 75 | 76 | (This isn't a great explanation & there's a lot more to Theme UI than this. If you'd be interested in more writing from me on using Theme UI, let me know!) 77 | 78 | ## The goods are on Glitch 79 | 80 | I developed the site 100% on [Glitch](https://glitch.com), without setting it up locally. Big fan. https://glitch.com/~hackpenn 81 | 82 | If you want to set up a site with the same basic tech stack, remix my starter project here: https://glitch.com/~next-mdx-rebass-theme-ui 83 | 84 | If you want to see all the code for the site in one place, check out [this file first](https://github.com/hackpenn/site/commit/5f55ae915a6e4762d8e2641383000f5770960e49#diff-63823233e264ed6abbcc6f7ea0ef10bd), then scroll up through the rest. 85 | 86 | If you want to see more projects from Hack Pennsylvania, I've put a few in [a Collection on Glitch](https://glitch.com/@lachlanjc/hack-pennsylvania). 87 | 88 | --- 89 | 90 | Questions/feedback? Email me: [lachlan@hackpenn.com](mailto:lachlan@hackpenn.com) 91 | -------------------------------------------------------------------------------- /sheets/2019-08-16_icons.mdx: -------------------------------------------------------------------------------- 1 | # [`@hackclub/icons`](https://github.com/hackclub/icons) 2 | 3 | Future name: **Ultracons** 4 | 5 | ## Wishlist 6 | 7 | - Phone 8 | - Consistent fill/outline for social networks 9 | - Transit 10 | - Updated Facebook 11 | 12 | ## Prior art 13 | 14 | - https://github.com/pricelinelabs/design-system/tree/master/packages/icons 15 | -------------------------------------------------------------------------------- /sheets/2019-08-20.mdx: -------------------------------------------------------------------------------- 1 | # Tuesday, August 20, 2019 2 | 3 | - Deployed new [AngelHacks site](https://angelhacks.org) ([source code](https://github.com/angelhacks/site)) 4 | - Added dark mode, site-wide AMP support, & added Hooks on [Gasp!](https://gasp.now.sh) 5 | - Fixed reaction sorting on [hackclub-emoji](https://hackclub-emoji.now.sh) 6 | - Minor updates to my [personal site](https://lachlanjc.me) 7 | -------------------------------------------------------------------------------- /sheets/2019-08-20_hack_club_first_meeting.mdx: -------------------------------------------------------------------------------- 1 | import { List } from '../components/blocks' 2 | 3 | # First meeting @ Hack Club 4 | 5 | My best first meeting was one of my most fun meetings ever. I had 40+ students crowded in one room—we needed chairs from other classrooms, some kids were still perched on windowsills—and the energy was visible everywhere. 6 | 7 | ## What to teach 8 | 9 | I highly highly recommend teaching web development, even if your club team is new to it. One of the great things about building a website is, you don’t have to install anything or become familiar with a terminal, & as soon as you write text, you see it on the screen. More broadly, you’re making something everyone has used before, so it breaks down an existing barrier—Facebook & Reddit & Gmail are all written using these exact same technologies, writing these same tags, whereas even if a program is doing something cool, it’s to hard to relate to it since it’s locked inside a terminal. 10 | 11 | ## Structure 12 | 13 | I super recommend running Hack Club’s Personal Website workshop. It’s made for beginners with a foundation of HTML & CSS. 14 | 15 | [Personal Website - Hack Club Workshops](https://hack.af/intro) 16 | 17 | > Short link for workshop: [**hack.af/intro**](https://hack.af/intro) 18 | 19 | ### Starting talk 20 | 21 | Welcome! Thank you all so much for being here at our first Hack Club meeting—seriously, it means the world to me. I'm Lachlan Campbell (they/them pronouns) & [introduce others]. First, I want a quick pulse. Raise your hands. 22 | 23 | - How many of you have written any code? 24 | - Published a website? Game? 25 | - How many people have a current website online with users? 26 | 27 | By the time you leave today, everyone will have a website they built from scratch online. 28 | 29 | I think coding is the closest thing we’ve got to a superpower, & I’m here because I want all of you to experience this. Just as a high schooler, you can have millions of people using things you make. But CS classes aren't inspiring. You learn fundamentals, maybe make something months later. We want you to make something in an hour here. 30 | 31 | At this workshop, you’re going to learn by necessity, jump off the deep end, learn on your own as you go. I’m not giving you a lecture on websites—you’ll have an hour with a self-guided tutorial, then a bunch of you will demo what you make. You don't want to just follow the tutorial though—since everyone’s going to demo, you want your site to stand out. Doing just what’s in the tutorial is failing here—you should go way above & beyond. 32 | 33 | You’re going to build on your own now! Whenever you've got questions, I’m here to help. Thanks again for being here. 34 | 35 | ### Schedule 36 | 37 | If you’re working with about an hour, here’s a quick breakdown of time: 38 | 39 | * 5 minutes: opening talk 40 | * 45 minutes: members’ work time 41 | * 10 minutes: demo members’ sites 42 | 43 | ### Example sites 44 | 45 | 46 | 47 | * https://freevbucks--herbertchen.repl.co 48 | * https://foodietruckee--lorence0304.repl.co 49 | * https://votebolbi--adamahmad.repl.co 50 | * https://shag--cody-l.repl.co 51 | * https://my-pillow.aplomado-falcon.repl.co 52 | * https://wisecoder--vuklmi.repl.co 53 | * https://gentleviolentexternalcommand--darshank.repl.co 54 | * https://website--denize-patricpa.repl.co 55 | * https://spookyseason--milesvilke.repl.co 56 | * https://blissfulelasticforce--bellachip.repl.co 57 | * https://jinyoungie.sulutemirova.repl.co 58 | * https://fumblingdiscreterevisioncontrol--venywiijaya.repl.co 59 | * https://freevbucks--herbertchen.repl.co 60 | 61 | 62 | -------------------------------------------------------------------------------- /sheets/2019-08-20_hack_club_site.mdx: -------------------------------------------------------------------------------- 1 | import { ShortcutsList } from '../components/blocks' 2 | 3 | # A website for your Hack Club 4 | 5 | ## Getting a hackclub.com subdomain 6 | 7 | Any club leader can get access to a [hackclub.com](http://hackclub.com) subdomain for their club or hackathon website. To register or update yours, go to [leaders.hackclub.com](https://leaders.hackclub.com/), sign in with the email address on your application, & there’s a link to “add a subdomain.” A Hack Club staff member will approve your request shortly. 8 | 9 | Fun fact: all of Hack Club’s DNS is [open source on GitHub](https://github.com/hackclub/dns)! 10 | 11 | If you have issues, ping @msw on Slack. 12 | 13 | ## Site examples 14 | 15 | 16 | 17 | - [Mason Hack Club](https://masonhackclub.com/) 18 | - [WLHS Hack Club](https://wl.hackclub.com/) 19 | - [Hack Club CCA](https://cca.hackclub.com/) 20 | - [State High Hack Club](https://statehigh.hackclub.com) 21 | - [Wootton Hack Club](https://hack.wootton.club/) 22 | - [FHS Hack Club](https://www.fhshackclub.com/) 23 | - [PSHS Hack Club](https://pshs.hackclub.com/) 24 | - [Westborough](https://westborough.hackclub.com/) 25 | - [JV Hack Club](https://jv.hackclub.com/) 26 | 27 | 28 | 29 | ## Adding a Hack Club banner 30 | 31 | ![Hack Club banner](https://cdn.glitch.com/a9442788-bc48-44ab-bedd-832d738c93dd%2F5476C8E7-96BD-4411-BD63-126D5CF988CC-78cfbcb2-aab9-4bf6-9e45-2c88640a7e46.png?v=1566337711096) 32 | 33 | Hack Club has official banners for your club website to link to Hack Club. If you’re making a standard HTML site, here’s the code for adding a linked banner: [github.com/hackclub/hackclub#banners](https://github.com/hackclub/hackclub#banners) 34 | 35 | If you’re making a React site, we’ve got you covered with a custom component published on npm as `@hackclub/banner`. Here’s the docs: [hackclub.com/banner](https://hackclub.com/banner) 36 | 37 | ## My club site 38 | 39 | While you might be inclined to make a website for marketing your club, I haven’t found that to be very useful. (You could put the link on posters or announcements, but the reality is that few people will visit it unless you do something super exciting.) Instead, I make my club website as a utility during club meetings for members, with links to everything they need. 40 | 41 | I quickly discovered a few key things needed from my club site: 42 | 43 | - Quick links to Hack Club Workshops 44 | - Quick links to repl.it, Glitch, hackathon to register for, etc 45 | - A form for members to submit projects. I do demos at the end of every club meeting, & to avoid everyone dealing with A/V individually, I have everyone submit to me their project links on the site. It sends me an email, so I can then pull up the links on my laptop with the projector connected. 46 | - I use a free service called [Formspree](https://formspree.io), but [Basin](https://usebasin.com/) is another free alternative. 47 | 48 | > [**State High Hack Club**](https://statehigh.hackclub.com) 49 | 50 | I made my site using React/[Gatsby](https://www.gatsbyjs.org/) & the [Hack Club Design System](https://design.hackclub.com/). It’s entirely open source, if you’d like to remix it: 51 | 52 | > [**SCHacks/site**](https://github.com/schacks/site) 53 | 54 | Here was the first version of the site ([source code](https://github.com/SCHacks/site/blob/e358fbd73a095f728843f19ff4e13f61cfa4d460/index.html)) & the [second version](https://5ba46bd1dd28ef740fcf3e9e--schacks.netlify.com/) ([source code](https://github.com/SCHacks/site/tree/8446a00fded449ee50110ec6182153d0c98596d4)): 55 | 56 | ![First site during summertime](https://cdn.glitch.com/a9442788-bc48-44ab-bedd-832d738c93dd%2Fsummertime-f61edb30-98c7-41ea-b47f-dc96aa8ca0c5.png?v=1566337710486) 57 | 58 | ![Old version of site](https://cdn.glitch.com/a9442788-bc48-44ab-bedd-832d738c93dd%2F7846171E-3A7D-4926-B862-408C09879F1A-6830d2ae-972b-4a77-838a-c7e68a04660c.jpg?v=1566337713150) 59 | -------------------------------------------------------------------------------- /sheets/2019-08-22_learning_web_design.mdx: -------------------------------------------------------------------------------- 1 | import { LinkList } from '../components/blocks' 2 | 3 | # Learning web design 4 | 5 | Someone in the [Hack Club Slack](https://hackclub.com/community) recently asked me for advice on learning web design. 6 | 7 | Here's what I said (edited). 8 | 9 | ___ 10 | 11 | The #1 thing, beyond any articles, is practice. I’ve made literally thousands of webpages, basically all from scratch. You just need to design a lot of things, & coding everything yourself, regardless of how it looks in the beginning, will be immensely more useful than using a template to pump out products that look better but you didn’t write all the code for. CSS is incredibly complicated, & the only real way to learn it is just using it long-term. 12 | 13 | Try adding some constraints. I did a lot of these exercises & they genuinely helped me. Design a website with one font in one font weight—you’ll need to flex your white space & hierarchy muscles to make it communicate well. Design a website with just black & white, you’ll need a super clear hierarchy to draw attention. etc 14 | 15 | I also would recommend staying away from designing in Figma/Sketch/Photoshop first. There, you’re designing a simulation of a webpage, & I think it’s totally different.[^1] Plus, once you’ve finished a mockup in Figma, you still haven’t shipped a real product—again, it’s a simulation—& you learn nothing about the code side. Design in the browser, with real code ([CodePen](https://codepen.io) & [Glitch](https://glitch.com) are great tools for this), & you’ll end up shipping real sites you can put in your portfolio, learn more, & I think make better results. There’s great uses for tools like Figma, like designing logos/assets/more complicated mockups, but for learning web design, write the code. 16 | 17 | [^1]: See Jason Fried’s [Why we skip Photoshop](https://signalvnoise.com/posts/1061-why-we-skip-photoshop) for more. It’s from 2008 but still totally true. 18 | 19 | ## Frameworks 20 | 21 | CSS frameworks/UI kits like Bulma & Bootstrap are totally valid for quickly building an app UI, but if you’re trying to learn web design, I suggest learning from them but not using them—since they make pre-packaged components, it’s easy to rely on them. Instead, make your own cards & buttons, & find out what you need to do that. 22 | 23 | Two frameworks that I do suggest checking out/using are [Tachyons](http://tachyons.io) & [Basscss](https://basscss.com). They both have a very similar idea—one class, like `.bold`, should do one thing, like make text bold. Combine `.bold` `.red` `.h1` & you’ve got big, red, bold text. They empower you to iterate quickly on design, while leaving the specific design choices up to you. 24 | 25 | ## Specific resources 26 | 27 | I learned a _ton_ from David Kadavy’s (now a bit old) [Design for Hackers](https://g.co/kgs/brfr1V), which is about super-applicable design principles. He used to do a summer email course called “Summer of Design” that was great. 28 | 29 | I highly suggest keeping tabs on some blogs like [CSS-Tricks](https://css-tricks.com/), too—you’ll learn small things you can do with CSS on a regular basis that you can add to your sites. 30 | 31 | Read the spec. You’ll find out about a ton of tags, CSS properties, & uses for them you didn’t know existed. 32 | 33 | [Steve Schoger’s Twitter](https://mobile.twitter.com/i/moments/879086180909764608?lang=en) is amazing. 34 | [This article](https://www.freecodecamp.org/news/fundamental-design-principles-for-non-designers-ad34c30caa7/) is worth a read. 35 | 36 | [@jxnblk](https://jxnblk.com) is a personal hero in the world of web design for me. Two of his articles on typography really shifted how I think about it: 37 | 38 | 39 | 40 | * [I’m Sick of Your Tiny, Tiny Type](https://jxnblk.com/blog/im-sick-of-your-tiny-tiny-type/) 41 | * [Mathematical Web Typography](https://jxnblk.com/blog/mathematical-web-typography/) 42 | 43 | 44 | 45 | (Of course, [design is still about words](https://signalvnoise.com/posts/3404-reminder-design-is-still-about-words). [Start in the middle](http://37signals.com/gettingreal/ch09_Epicenter_Design.php), with real text, not lorem ipsum.) 46 | 47 | Basically all of [Adam Morse](https://mrmrs.cc)'s blog is a must-read, especially his early articles about CSS. (He built the Tachyons framework.) 48 | 49 | 50 | 51 | * [The Veil of Ignorance](http://mrmrs.cc/writing/the-veil-of-ignorance/) 52 | * [What is CSS for?](http://mrmrs.cc/writing/what-is-css-for/) 53 | * [Good CSS](http://mrmrs.cc/writing/good-css/) 54 | * [What are Classes for?](http://mrmrs.cc/writing/what-are-classes-for/) 55 | * [Learning CSS](http://mrmrs.cc/writing/learning-css/) 56 | * [Mobile-first CSS](http://mrmrs.cc/writing/mobile-first-css/) 57 | * [How to line-height](http://mrmrs.cc/writing/line-height/) 58 | * [Webfonts](http://mrmrs.cc/writing/webfonts/) 59 | * [Css and Scalability](http://mrmrs.cc/writing/scalable-css/) 60 | * [Developing UI](http://mrmrs.cc/writing/developing-ui/) 61 | * [Design Systems](http://mrmrs.cc/writing/design-systems/) 62 | 63 | 64 | -------------------------------------------------------------------------------- /sheets/2019-08-22_pennsylvania.mdx: -------------------------------------------------------------------------------- 1 | # Pennsylvania 2 | 3 | I used to wish I wasn’t from a [small town](https://en.wikipedia.org/wiki/State_College,_Pennsylvania) in the middle of Pennsylvania. I could be from, you know, _New York_. 4 | 5 | (Spoiler: this weekend I’m moving to NYC to attend NYU!) 6 | 7 | But in recent years, even though my primary [communities](https://hackclub.com/community/) are absolutely online, I’ve become really proud of where I’m from. Sure, it’s a small college town, & I live quite literally in the middle of a forest, but it’s awesome here. We have space here. In the summer, the sound outside is the cicadas in the trees. When it snows, it’s dead quiet. 8 | 9 | ![Snowy forest](https://cdn.glitch.com/a9442788-bc48-44ab-bedd-832d738c93dd%2FIMG_0755.jpeg) 10 | 11 | I went to a public high school where plenty of kids are farmers.[^1] We have school off for deer hunting (actually). 12 | 13 | [^1]: Farmers are going to save us from starving as the apocalypse becomes reality. Support independent farmers! 14 | 15 | As I’ve engaged more with my local community, I’ve become even more proud of it. My friends & I led a campaign to open gender-neutral bathrooms for all students at our high school. I’m part of a local LGBTQ+ youth group. Last winter I organized [Hack Pennsylvania](https://hackpenn.com), a 111-person high school hackathon during a blizzard. Students drove from all over the state (even Maryland!) to come. What we made happen was totally unique. 16 | 17 | Looking ahead at my life, the cities will always be there, bustling, & growing. I’m incredibly excited (if a little nervous) to go to NYC—it’s the place I most want to be at this stage of my life. But being able to come home under the trees, I wouldn’t trade that for anything else. It’s an incredible privilege. 18 | 19 | It felt good to update my [personal site](https://lachlanjc.me) the other day. `Proudly from Pennsylvania. Now based in NYC.` 20 | -------------------------------------------------------------------------------- /sheets/2019-08-23_the_man.mdx: -------------------------------------------------------------------------------- 1 | import Embed from 'react-song-embed' 2 | 3 | # The Man 4 | 5 | From Taylor Swift’s new song “The Man”: 6 | 7 | > What’s that like? I’m so sick of running as fast I can 8 | > Wondering if I’d get there quicker 9 | > If I was a man 10 | > And I’m so sick of them coming at me again 11 | > ‘Cause if I was a man… 12 | > Then I’d be the man 13 | 14 | 15 | 16 | ___ 17 | 18 | For 16 years, I used to think I was a boy. It was fine.[^1] 19 | 20 | [^1]: I’m lucky to not experience significant gender dysphoria—I feel pretty confident in my body, so that has never been an internal force for me. 21 | 22 | My dominant experience with gender, before I started questioning my identity, wasn’t really an experience. I never really thought about gender. Honestly, I assumed it was like that for everyone.[^2] Either you were born with a gender you felt good with or you know you’re the opposite gender & want to transition, right? 23 | 24 | [^2]: Especially being white in a mostly-white community. 25 | 26 | But that’s not the only gender experience, as it turns out. Since coming out as nonbinary, I’ve begun to recognize how one part of (cis) male privilege is just not thinking about gender. When you have the default gender experience, gender doesn’t have to be a big force in your life. 27 | 28 | Every time you walk in somewhere, you’re not evaluating if & how much you can express your gender or if you need to mask yourself to stay safe. You can walk around a city at night as yourself, not covered-up. When you meet someone, you don’t have to find a time to mention or subtly hint your pronouns so they don’t use ones that make you want to leap out of your body. In fact, people…already know your gender, because they just assumed…& it wasn’t an issue? (I guess it’s been long enough since I had that experience.) 29 | 30 | > What’s that like? / If I was a man / then I’d be the man. 31 | 32 | Boys will ~~be boys~~…become bois, apparently. 33 | -------------------------------------------------------------------------------- /sheets/2019-09-05_windy_city_hacks_newsletters.mdx: -------------------------------------------------------------------------------- 1 | import { Box } from 'theme-ui' 2 | import { ShortcutsList } from '../components/blocks' 3 | 4 | # Windy City Hacks Newsletters 5 | 6 | For [Windy City Hacks](https://windyhacks.com/), I wanted to send out custom HTML newsletters, entirely from my iPad. The process I came up with isn’t necessarily _good_, but here’s how I did it. 7 | 8 | Note: This will require significant customization if you’re intending to use a similar process. Also, be prepared to spend several hours per newsletter, start to finish. 9 | 10 | I started by using a [Zurb responsive email template](https://zurb.com/playground/responsive-email-templates), because CSS + emails is a horrible combination. I spent several hours updating the design to the Windy brand, testing in various clients, etc. 11 | 12 | [**Check out a sample newsletter**](https://2019-site.windyhacks.com/newsletters/first.html) 13 | 14 | ## Writing a newsletter 15 | 16 | ### Apps I used 17 | 18 | - Shortcuts 19 | - Drafts 20 | - Working Copy 21 | - Numbers 22 | 23 | ### Shortcuts I made 24 | 25 | 26 | 27 | - [Compose Newsletter](https://www.icloud.com/shortcuts/fb0d8b55d9014266812942c83b6fbc97) 28 | - [Newsletter HTML](https://www.icloud.com/shortcuts/af62700c88d7409ab231aa37beebcfd4) 29 | - [Make Plain Text Newsletter](https://www.icloud.com/shortcuts/21225bcd5b494ad8acf0b3272ee3dbb1) 30 | 31 | 32 | 33 | I started by composing the newsletters (with [Matthew](https://matthewstanciu.me)), focusing just on what we wanted to say. (Apple Notes works great here.) Remember, the content matters a whole lot more than the formatting :) 34 | 35 | ### Composing the HTML 36 | 37 | I then ran the `Compose Newsletter` Shortcut, which creates a new file in Working Copy at the correct path with the basic template. 38 | 39 | If you plug your iPad into an external display (USB-C → HDMI adapter if your monitor isn’t directly supported), Working Copy renders a preview of the HTML on the external display, & the code on your iPad. Fab! 40 | 41 | I’d set up Split View with Working Copy next to Shortcuts, & Notes in Slide Over. 42 | 43 | The next part was always super painful, because of the need to generate HTML with inline styles. For each line of text, I’d run the `Newsletter HTML` Shortcut, fill out whatever options, & paste into the Working Copy editor. Inline formatting like bold or links must be handled individually. 44 | 45 | ### Generating a plain text copy 46 | 47 | Sending plain text alongside your HTML email is good form. Unfortunately, I couldn’t just use the original text, because I’d always edit it while making the HTML version/reading it in my client. 48 | 49 | Select all the HTML in the Working Copy editor, Share → Shortcuts → Make Plain Text Newsletter, then edit in Drafts. 50 | 51 | ## Sending a newsletter 52 | 53 | If you’re using [Hack Club Bank](https://hackclub.com/bank/), Hack Club has an installation of [Sendy](https://sendy.co), which is honestly the best newsletter software I’ve ever used. Highly recommend it over MailChimp & friends. 54 | 55 | > Note: I’ve previously tried TinyLetter for a hackathon, but if anyone replies to your email with an attachment (like their signed waiver form), you can’t see the attachment. This is a big no. 56 | 57 | ### Generating the recipient list 58 | 59 | Our attendees were stored in Airtable, so I did whatever relevant filtering there, then exported a CSV. (This is only available via the website because Airtable’s iPad app sucks, but the website is becoming more usable via iPadOS). Then I’d open the CSV in Numbers to manipulate the columns (renaming the emergency contact name to `name`, deleting unused columns, etc), re-export as CSV, & upload the list to Sendy. 60 | 61 | This is super tiresome, but there wasn’t a clear pattern to automate, & CSV is rough to automate in Shortcuts. 62 | 63 | ### Sending it out 64 | 65 | This part is pretty simple! Paste the plain text & HTML into Sendy’s editor. They’ve got options for scheduling, sending test emails, etc. 66 | 67 | ### Test your newsletter. Always! 68 | 69 | Never, ever send out a newsletter without sending yourself a test copy, sometimes several, first. I would undoubtedly discover a typo, need to make an edit, find a bug, etc. 70 | 71 | 72 | I hope this was somewhat useful. It’s not a recommendation to use this system, merely documentation of what I did if you wanted to recreate it for your own hackathon, club, etc. 73 | 74 | Happy emailing! 💌 75 | -------------------------------------------------------------------------------- /sheets/2019-09-06_keeping_a_notebook.mdx: -------------------------------------------------------------------------------- 1 | # Keeping a Notebook 2 | 3 | Like many other web developers, I’ve built myself a blog more than once, then didn’t write in it. 4 | 5 | I started keeping my [Notebook](/) about a month ago. It’s definitely the most continuous blog I’ve ever kept. 6 | 7 | With other blogs, I internally would set this high bar that everything on my blog needed to be perfect—a long-lasting, authoritative institution of a webpage. Why would I want to publish something that’s not perfect? 8 | 9 | (Because I don’t have time or care enough for that!) 10 | 11 | The Notebook format is just like publishing GitHub Gists. They’re self-contained, Markdown-files-turned-webpages.[^1] 12 | 13 | With Notebook, I often publish a post within 30 minutes of thinking of it. I open [Drafts](https://getdrafts.com), start writing, re-read/edit, then [publish](/2019-09-05_publishing_notebook_in_mdx_via_ipad/).[^2] 14 | 15 | Sometimes I want to write about how [I’m a big iPad nerd](/2019-09-05_windy_city_hacks_newsletters/). Sometimes it’s about [my design philosophy](/2019-09-06_my_websites_look_the_same/). Sometimes it’s [thoughts on gender experience](/2019-08-23_the_man/). My Notebook isn’t an Apple blog, a design blog, or a gender blog. It’s _my place to publish on the internet_. 16 | 17 | There are no comments, & I don’t feel any judgement about what I post.[^3] I’m not putting my thoughts into the feeds of a thousand people I’m scared of disappointing. I can just be me. If no one reads it, or if a post were to go viral, I don’t especially care either way. 18 | 19 | There’s a much lower bar to “writing in my notebook” than “publishing on a blog”[^4]—especially when that’s a site I don’t think very many people look at. I can get something out quickly, without feeling like it needs to be perfect. Posts are written exactly the way I speak. They have a date, but no timestamp. 20 | 21 | If people are curious what I’m thinking about or working on, it’s public here. Or if it’s just a quick document I want to share, I can put that here. Sometimes I write several posts, one after another. I don’t set expectations about what you’ll get or how often or why. It’s just me. This is my Notebook. 22 | 23 | [^1]: I love being able to publish a decent-looking webpage on my own domain super super easily! [Gatsby](https://www.gatsbyjs.org) + [MDX](https://mdxjs.com/) is amazing. 24 | [^2]: If I can’t/don’t want to write a post immediately, I keep a short list (in [Things](https://culturedcode.com/things/), before someone asks) of ideas. Honestly though, I’m way less likely to write a post if it sits on that list for a bit than if I’ve just had the idea & run with the spark. 25 | [^3]: Including the fact that I want to use footnotes kind of often, so I do. 26 | [^4]: Though this is pretty roughly a blog, I like [@jxnblk on non-traditional journals](https://jxnblk.com/blog/microbeats-is-the-best-journal-ive-ever-kept/). 27 | -------------------------------------------------------------------------------- /sheets/2019-09-06_my_websites_look_the_same.mdx: -------------------------------------------------------------------------------- 1 | # My websites look the same 2 | 3 | If you look at the sites I make in chronological order, many are fairly similar to one another. I’ll use the same tech stack & sometimes aesthetics for several sites in a row (examples from this summer: [CO2](https://co2.now.sh) → [hackclub-emoji](https://hackclub-emoji.now.sh), [Hack Pennsylvania recap](https://hackpenn.com) → [personal site](https://lachlanjc.com), [Notebook](https://notebook-dagcpvw5y.now.sh/) → [IMA](https://ima.lachlanjc.com)). They’re [Iterations on a Theme](https://jxnblk.com/blog/iterations-on-a-theme/), one might say. 4 | 5 | I am a designer, not an artist. My sites are not artwork for a gallery, they are designed with a specific purpose & goal in mind. Each one is for a different audience—if the [Windy City Hacks](https://windyhacks.com) site shares a lot with [Hack Pennsylvania](https://hackpenn.com), so be it. The goal of those sites is getting high schoolers hundreds of miles from one another to sign up for an event, & they don’t care about the design. As such, making each site totally unique is not a priority for me. 6 | 7 | This is partly because I rarely start sites’ code from scratch. Each site is for express purpose of communicating information, & making them quickly & pretty “feature-complete” (design system/theme/reusable components, social cards/metadata, dark mode, server-rendering, basic accessibility, etc) is often important. The Windy website was forked from the Penn website which was forked from the [Hack Happy Valley](https://hackhappyvalley.com) website which borrowed components from [Hack Club](https://hackclub.com). 8 | 9 | I make myself [starter templates](https://github.com/lachlanjc/next-mdx-rebass-theme-ui), & often just clone or remix a previous site (I code mostly on [Glitch](https://glitch.com) these days! from my iPad). For small ideas, giving myself an easier jumping off point is often critical to making the project happen—depending on how important a project is (Hack Penn: mission-critical, hackclub-emoji: throwaway idea), if I had to start from scratch every time, the small ideas would never happen. 10 | 11 | I’m not ashamed of making websites that look the same, & I don’t think you should either. If the websites are making the web & world a better place, making more of them faster is just clearly better. No one cares if each one I started from scratch. 12 | -------------------------------------------------------------------------------- /sheets/2019-09-10_why_i_care_about_apple.mdx: -------------------------------------------------------------------------------- 1 | import Embed from 'react-song-embed' 2 | 3 | # Why I care about Apple 4 | 5 | Writing this, I was listening to this beautiful song: 6 | 7 | 8 | 9 | --- 10 | 11 | Today’s [the big iPhone day](https://www.apple.com/apple-events/). Anyone who knows me personally or looks at my Twitter Likes knows I am a massive Apple nerd. I am constantly up-to-date on every piece of news regarding Apple, especially iPad. The news consumption is absolutely fanatical. 12 | 13 | The world’s largest, trillion-dollar corporation would seem to be _not_ up my alley, in theory. I believe in many social & political ideas antithetical to the existence of its modern incarnation. 14 | 15 | Nevertheless. I am a nerd whose level of obsession can be off the charts. Truly, unashamedly, insanely deep nerdery. I get extremely excited about vanishingly minute details executed flawlessly. Let’s walk through an Apple Store together & you will begin to find out the extent. I can talk about rounded corners for an _extended period_. 16 | 17 | I’m not obsessed with the company, or its employees, or Steve Jobs, or Tim Cook for that matter. What comes out of the company, however, is such a spectacular execution of a vision of suite of deeply-refined, beautifully-integrated products, from iPad packaging to Handoff to AirPlay to Apple Pencil. Wherever you venture, there are Apple hardware, software, & services to help. 18 | 19 | “But they’re just tools.” Yes, indeed. But Apple products enable all the work I do. I am well aware there are other devices with largely similar functionality, but none I have ever been significantly excited by. I don’t need to be excited by my tools, but I do like to be. It’s often like a deeply large-scale version of cleaning your desk & then looking for a new desk to replace it with instead of sitting at the desk to do [homework](https://ima.lachlanjc.me). 20 | 21 | Yes, there are very real social & economic & environmental impacts of Apple, & I am aware of them. By buying Apple products, I can only hope [the impact I create with them outweighs](/2018-11-04_macbook_pro/) those effects. There also aren’t truly alternatives to Apple devices that allow me to do my work in the world without social & environmental impact, & I don’t think abandoning all electronics until their production has no environmental impact is the answer. I also don’t believe in boycotting the people pointing out those issues because they’re contributing to the issues by participating in the conversation (e.g. wearing clothes while investigating the environmental effects of fast fashion). These conversations are critical, & we should foster them, but in a productive way. 22 | 23 | We can’t all be perfect // no one is perfect, I guess. Realistically, I can’t zero out my carbon footprint, though I wish I could. I can’t fix the injustices faced by trans people across the world. While I try to chip away at problems in front me, I need a balance of work & entertainment. Unfortunately, due to the ways of my monkey brain, I can’t be outputting world-changing work all the time. Or even most of the time. It’s a small chunk of my time that creates any real impact. Over time, as I learn more about the world & do more things, I hope & try to make that chunk increase. 24 | 25 | So in the meantime, the world is burning, & Apple is the one mini-planet I can nerd over to no end & mostly ignore the consequences. This is because, on a global scale, I am obscenely privileged. But, those [rounded corners](https://en.wikipedia.org/wiki/Squircle)… 26 | -------------------------------------------------------------------------------- /sheets/2019-09-12_ipad_coding_with_glitch.mdx: -------------------------------------------------------------------------------- 1 | import { Tweet } from '../components/blocks' 2 | 3 | # Coding from iPad with Glitch 4 | 5 | I use my iPad Pro ~9 hrs/day, according to iOS Screen Time. I haven’t opened my MacBook in 2 weeks. It’s great. 6 | 7 | How do I code from my iPad? [Glitch](https://glitch.com) in Safari. 8 | 9 | How do you get started with Glitch? It’s really, really simple. You go there, tap New Project, & start coding. You can see a live preview as you work. You can code with friends, realtime. There’s logs & a terminal. You can connect a custom domain, download your code, import or export to GitHub. Glitch even lets you know when your npm packages have updates. It’s amazing. 10 | 11 | I’ve made the [Hack Penn recap site](https://hackpenn.com), the [AngelHacks site](https://angelhacks.org), [this site & most of its posts](/2019-09-05_publishing_notebook_in_mdx_via_ipad/), & [many more](https://glitch.com/@lachlanjc) on Glitch recently. 12 | 13 | ## Drawbacks 14 | 15 | There are three things I seriously miss: [Prettier](https://prettier.io), Vim, & the web inspector. Prettier I seriously hope Glitch will integrate, Vim I don’t expect them to, & the web inspector Apple just needs to build. 16 | 17 | I’m doing without those for now because the iPad support so significantly outweighs them for my personal workflow. If you’re coding full-time (I’m not anymore, because college), I would absolutely try an alternative. 18 | 19 | I’ve also documented a handful of bugs with Glitch on iPad & notified their amazing team about them, so I’m hoping it’ll be less rough around the edges soon. 20 | 21 | (Also, if you’re using Gatsby or Next.js—I use them for essentially every site—[the end of this post has a tip to improve the experience](/2019-09-05_publishing_notebook_in_mdx_via_ipad/).) 22 | 23 | ## Alternatives 24 | 25 | There are a bunch of other ways to code from iPad, too! 26 | 27 | If you’re not working offline & used to a terminal text editor, grab an app like [Blink Shell](https://www.blink.sh/), spin up your own server or DigitalOcean/EC2/etc, & you’re ready to go. 28 | 29 | This is less portable, but via @jxnblk, you can [run anything on a connected Raspberry Pi over USB-C](https://twitter.com/jxnblk/status/1147555688933154816?s=20). 30 | 31 | I’m not running the iPadOS/iOS beta on my primary devices, but once iPadOS is released, we’ll have even more tools available. [Owen Williams is using full VSCode with it.](https://medium.com/@ow/its-finally-possible-to-code-web-apps-on-an-ipad-pro-90ad9c1fb59a) 32 | -------------------------------------------------------------------------------- /sheets/2019-09-23_digital_handwriting.mdx: -------------------------------------------------------------------------------- 1 | import { Handwriting } from '../components/blocks' 2 | 3 | # Digital handwriting 4 | 5 | 6 | digital handwriting 7 | 8 | -------------------------------------------------------------------------------- /sheets/2019-09-24_ipados_release.mdx: -------------------------------------------------------------------------------- 1 | # iPadOS Release 2 | 3 | Today, finally, iPadOS sees its general release. I’ve of course been working my way through [the extensive MacStories review](http://macstories.net/stories/ios-and-ipados-13-the-macstories-review/) (70k words), which if you like pushing the limits of iOS, is an indispensable & enjoyable read. 4 | 5 | I’ve been using it all summer on my secondary iPad (Pro 10.5”), but it has not had the performance & support necessary to run on my primary device. You can bet I’ll install the public release on both devices in the first five minutes, though. 6 | 7 | To me, it feels very much like the release of iOS 11, where monumental upgrades changed the nature of what an iPad could do. In iOS 11, the iPad bloomed, gaining the Files app, drag and drop, the Dock, & [the current multitasking system](https://www.macstories.net/stories/ios-11-the-macstories-review/16/#the-spatiality-of-ios-11-for-ipad), among many other changes. (For a dark period prior, we were stuck using iOS 9 & 10 on the iPad—the only significantly differentiating feature from the iPhone operating system being Split View, was invoked through that [godawful app picker](https://www.macstories.net/stories/ios-9-review/11/#split-view).) The iOS 11 system is remarkably easy to understand, fast to use, & fluid in operation. Nonetheless, spending hundreds/thousands of hours using it since its launch, [flaws have revealed themselves](https://www.macstories.net/stories/beyond-the-tablet/7/#the-problems-with-ipad-multitasking). Using one app in multiple contexts is not possible, & the simplicity imposes hard limits on advanced use. 8 | 9 | With iPadOS, the iPad blooms so much further. Multi-window, the drastic upgrades to the Files app, & desktop-class browsing in Safari are all critical, platform-changing improvements, advancing the capabilities of iPad significantly. Carrying around a 12.9” iPad Pro with USB-C & iPadOS, I no longer have even occasional need for a Mac or hacky workarounds for simple file operations involving web downloads, archiving, and external drives. It just works, one could say. 10 | 11 | Meanwhile, coming in spades alongside the headline features, parallel to those of iOS 11, dozens of small improvements enhance the iPad experience corner-to-corner. iOS 11 brought Instant Notes (tap Apple Pencil to the lock screen for a new note), & now we have Full-Page Screenshots, which I’ve already made extensive use of this summer for quick web design feedback. iPadOS also brings a suite of new developer tools, like PencilKit, which I’m incredibly excited to see apps make use of. 12 | 13 | I would be remiss to not mention the massive upgrades to the Shortcuts app, from its new editor & UI to the streamlining of Siri Shortcuts to the app being pre-installed by default. With millions of users joining the system, & a dramatically simpler experience for creating shortcuts, I think many more users will discover the wonders of personal automation. 14 | 15 | If Apple succeeds, the introduction of iPadOS today will be remembered as a critical turning point in the development of the next-generation personal computer. It’s wildly cool to watch—& join in on—the development of this new computer in public. The device the world clamored for in 2010 has blossomed into a computer for millions, simplifying & modernizing the ideas of traditional computers for new generations using new form factors of computer. I can’t wait to continue pushing the new limits. 16 | -------------------------------------------------------------------------------- /sheets/2019-09-24_lachlan_ultra.mdx: -------------------------------------------------------------------------------- 1 | # Lachlan Ultra 2 | 3 | I use the word “ultra” a lot, primary to describe myself. It’s an extracted prefix I’ve assigned my own meaning to. Ultra-passionate, ultra-weird, ultra-forward, ultra…ultra. 4 | 5 | I feel ultra wearing bright red pants. I feel ultra belting out lyrics to Bloom or Ultralife or Generation Why. I feel ultra spearheading initiatives I care about. 6 | 7 | It’s the opposite of small & weak. I enjoy making big statements, big movements, loud dancing with lots of gusto. I love making it ultra. 8 | 9 | No one can say I can’t have a word for my style, & over time one became clear to me. So it’s Lachlan Ultra, & I love it. You can do it too, & there’s no one to stop you. 10 | -------------------------------------------------------------------------------- /sheets/2019-09-24_learning_ios_development.mdx: -------------------------------------------------------------------------------- 1 | # Learning iOS development 2 | 3 | When I first started coding, I assumed after making websites for a bit I would graduate into real coding—native apps, probably for the iPhone. I’ve made several small attempts over the years to begin learning iOS development, but nothing ever really stuck for me. 4 | 5 | I have a good dozen solid ideas for iOS apps (productivity, utility, personal) that can only be well-executed with a native Swift-powered app. 6 | 7 | Every tutorial I’ve read just…doesn’t really click. SwiftUI will absolutely lower the barrier to entry, especially once Catalina is released & it’s natively easy to use. But so far nothing’s really worked for me. 8 | 9 | One of the biggest barriers for me is needing to use a Mac—I have only opened my MacBook a few times in the past month, all to run the Arduino IDE for one of my classes. The inability to develop native apps directly on iPad is disappointing (& in my opinion, kind of inexcusable at this point). 10 | 11 | At some point in my career, even though I’m not at all expecting to switch to it, I look forward to building some native apps. 12 | -------------------------------------------------------------------------------- /sheets/2019-09-24_notes_on_college.mdx: -------------------------------------------------------------------------------- 1 | # Notes on the college experience 2 | 3 | A semester of college, with classes five days a week, is simply too long (for me). I have ideas of things to make on a daily basis, but now have no time to actually create them, and frankly I’m the least busy I’ll be during all four years of this. How am I supposed to do this for four years, working for companies in the summers, & _not_ go do my own thing? As interesting as my classes are, not being able to create things directly from my soul is _stifling_. 4 | 5 | I do know incredibly smart college students who barely focus on the schoolwork & instead focus entirely on making things/developing outside school, & I really respect that. But personally, it just doesn’t make sense. I’m not going to college to please other people, I’m going because I think it makes sense for my life right now. There is no reason to spend many hours & a quarter million on an education you don’t focus on. 6 | 7 | That said, this schedule was clearly optimized for those of us who want to put 100% into our academics & nothing else. Though academic work has significant merits, I cannot subsist merely off academic work. I need to truly engage in things I truly love, very directly, very frequently. I’ve been doing that since before I was a toddler—making “art” projects, knitting, coding, organizing—& I cannot stop now. 8 | 9 | This was my bedroom as a young child: 10 | 11 | ![My room as a young child](https://cdn.glitch.com/a9442788-bc48-44ab-bedd-832d738c93dd%2Fcang10_01_2712_800p.jpg?v=1569353404918) 12 | 13 | I am sure finding a balance here is the key. I have intentions to start a club here at NYU, build various new projects, excel in my classes, take great care of my health, have a flourishing social life & career. It’s easy to get carried away in the vision of each of these pathways, but ultimately it’s the balance of all the elements that will lead to a happy/successful time here. 14 | -------------------------------------------------------------------------------- /sheets/2019-09-30_music_roundup_sep_2019.mdx: -------------------------------------------------------------------------------- 1 | import Embed from 'react-music-embed' 2 | 3 | # Music roundup, September 2019 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /sheets/2019-10-30_dash_plus_system.mdx: -------------------------------------------------------------------------------- 1 | # Dash/Plus system 2 | 3 | I first stumbled across [Patrick Rhone’s Dash/Plus System](https://patrickrhone.com/dashplus/) in 2017, but I thought of it again recently. It’s great for paper checklists. Maybe you’ll find it useful or remarkable. 4 | 5 | ![Note metadata representation scan](https://patrickrhone.com/images/notemetadata.JPG) 6 | 7 | ## Related tools 8 | 9 | - If you’re looking for Dash/Plus but for digital checklists, you might like [Taskpaper](https://mattgemmell.com/taskpaper-3/) 10 | - If you’re looking for that but for desktop notes, [nvALT](https://brettterpstra.com/projects/nvalt/) (& the upcoming [nvUltra](https://brettterpstra.com/2019/04/10/codename-nvultra/)) might be up your alley 11 | - On iOS, the closest tool to nvALT is absolutely [Drafts](https://getdrafts.com), which is my [daily text editor](https://www.macstories.net/reviews/drafts-5-the-macstories-review/) 12 | -------------------------------------------------------------------------------- /sheets/2019-10-31_music_roundup_october_2019.mdx: -------------------------------------------------------------------------------- 1 | import Embed from 'react-song-embed' 2 | 3 | # Music roundup, October 2019 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /sheets/2019-11-04_just_my_own_little_website.mdx: -------------------------------------------------------------------------------- 1 | # Just my own little website 2 | 3 | [Manuel Moreale recently wrote about the personal website](https://manuelmoreale.com/websites-and-complexity), and it struck a chord with me. I’ve had [my own website](https://lachlanjc.me) since 2014, hand-coded from the start, but I am in the vast minority of internet users. 4 | 5 | I’ve made maybe a dozen websites for non-technical friends and family members. For each site, if I were to code something, even using the lowest common-denominator, plain HTML + CSS ([Jonas Downey said “it’s okay not to use tools”](https://m.signalvnoise.com/its-ok-not-to-use-tools/)), they would never be able to make changes without asking me or learning to code themselves. 6 | 7 | So instead, I use Squarespace. I can get a customized Squarespace template with their materials up in two hours, the site will never go down, it will work for many years into the future with Squarespace’s constant updates, they can make quick edits, and I have nothing to worry about. We can upload their iPhone photos and someone else deals with resizing them. 8 | 9 | But it’s a Squarespace site. As a designer, my options are limited. If Squarespace shuts down, or they ever want to switch providers, they have no option but completely recreate the site some other way. They’re fully locked into the Squarespace service. 10 | 11 | I so wish this wasn’t the case. I wish there was a smoother gradient from setting up a simple portfolio/blog with no code to editing it manually. Of anything right now, [Gatsby](https://www.gatsbyjs.org) gives me the most hope—there’s certainly a promise of a future with a CMS, a Gatsby Theme, and simple hosting. 12 | 13 | But that is not the present reality whatsoever. A website I developed in Gatsby even a year ago is majorly out of date now—packages need to be updated, APIs have been deprecated, everything is dusty. Setting up a CMS requires choosing between a variety of services and their respective, often-unreliable community plugins. Last I used them, Gatsby Themes are unreliable, sparse, and poorly-documented. I’ll need to make them an account at a domain registrar, a hosting service, hook those things up, maybe a GitHub too. In two years, most parts of this system will probably have completely changed. 14 | 15 | I don’t know if no-code tools are even the dream here. At least in our present, where no-code tools can accomplish only a fraction of the (admittedly unlimited) possibilities of custom code, there is limited agency to come with setting up a no-code tool. It’s hard to see that changing significantly soon. 16 | 17 | Everyone I’ve made a Squarespace site for—even with the tool’s fairly constrained capabilities and tutorials—finds even that level of control of a website intimidating, and they rarely ever edit the sites without me. The promise of learning to code through a community like [Glitch](http://glitch.com) is nice, but few have the time or commitment for that. So right now, social media profiles are the extent of web publishing for the vast majority. 18 | 19 | We need to [take the power back](https://ia.net/topics/take-the-power-back) on the internet. People should have their own, non-corporate spaces on the web. But two decades in, we still haven’t built tools nearly accessible and extensible for the masses to truly utilize it. -------------------------------------------------------------------------------- /sheets/2019-11-05_custom_social_card_services.mdx: -------------------------------------------------------------------------------- 1 | import { Button } from 'theme-ui' 2 | 3 | # Custom social card image services 4 | 5 | Open Graph images are the image you sometimes see when sharing a link on iMessage, Slack, Twitter, etc. Though they can be any normal image, I first saw the idea of dynamically generating Open Graph images [from @levelsio/NomadList](https://levels.io/phantomjs-social-media-share-pictures/) a few years ago. 6 | 7 | Earlier this year, the indomitable ZEIT team [built a web service](https://zeit.co/blog/social-og-image-cards-as-a-service) for their own site’s social cards called [og-image](https://github.com/vercel/og-image), which I forked for [Hack Club’s Workshop Cards](https://hackclub-workshopcards.now.sh). Since then, they’ve rewritten the system to be simpler to run & deploy, so I used their latest version for my new projects. 8 | 9 | I’ve now created custom cards for all pages on both Notebook & my [IMA blog](https://ima.lachlanjc.com): 10 | 11 | ![Examples of my new social cards, composited with Photoshop for iPad](https://cloud-1tm0gjs2w.vercel.app/2021-01-13_8n9y3u809epu1r9qgdf2nh1xddz5vkgk.png) 12 | 13 | If you want to play with the systems, try them out here: 14 | 15 | 18 | 19 | 22 | 23 | Under the hood, a serverless Node.js function (hosted on [ZEIT Now](https://zeit.co/), of course) spins up a Puppeteer instance to render a webpage to an image file. The backend is written in TypeScript and the frontend uses [.dom](https://github.com/wavesoft/dot-dom). If you want to read more, [ZEIT has you covered](https://zeit.co/blog/social-og-image-cards-as-a-service#how-it-works). 24 | 25 | My changes: 26 | 27 | - I added a `caption` field, for displaying the course name and/or date 28 | - I removed the default images (ZEIT logos) 29 | - I edited the frontend, removing their image presets and custom width/height functionality and updating the style/typography 30 | - I remade the final card template (primarily static HTML/CSS) 31 | 32 | View the final source: 33 | 34 | 41 | 42 | 45 | 46 | If you’re looking to create your own, similar service for your own website/blog, I recommend forking [`notebook-cards`](https://github.com/lachlanjc/notebook-cards). `npm i`, `now dev`, then jump into the [`api/_lib/template.ts`](https://github.com/lachlanjc/notebook-cards/blob/main/api/_lib/template.ts) file to edit the template with your own content/styling. When it’s done, just deploy via `now` & you’re ready to go! 47 | 48 | You’ll then need to add the meta tags to links to your card service. This depends significantly on your site’s tech stack, but if you’re using React, I highly recommend making [a component](https://github.com/lachlanjc/notebook/blob/345afbf4b512e5d5f4c66a15fab1d1be8ce46297/src/components/meta.js) to wrap all the necessary tags. If you’re using Next.js, you’ll need their [`Head` component](https://nextjs.org/docs/api-reference/next/head), for Gatsby, [`react-helmet` and its associated plugin](https://www.gatsbyjs.org/docs/add-page-metadata/#using-react-helmet-and-gatsby-plugin-react-helmet). 49 | 50 | (On Notebook, I use a completely-ridiculous method of storing post names in slugs, so [the way I generate the metadata](https://github.com/lachlanjc/notebook/commit/345afbf4b512e5d5f4c66a15fab1d1be8ce46297#diff-34a6d62af0cf0b784f8444529f3130efR42) is confusing and not recommendable, but it works great for me.) 51 | 52 | One nice touch is that the service supports a dark mode, which you can automatically use for special posts. On Notebook, that’s my non-dated pages like [Tools](/tools/), and on IMA, I made midterm & final projects use dark mode. If you never touch it, that works too. 53 | 54 | Those are my systems, remixed from ZEIT’s & now open source for you to remix too. If you make your own version, let me know! 55 | -------------------------------------------------------------------------------- /sheets/2019-11-14_music_roundup_mid_nov_2019.mdx: -------------------------------------------------------------------------------- 1 | import Embed from 'react-song-embed' 2 | 3 | # Music roundup, mid Nov 2019 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /sheets/2019-11-30_music_roundup_late_november_2019.mdx: -------------------------------------------------------------------------------- 1 | import Embed from 'react-song-embed' 2 | 3 | # Music roundup, late November 2019 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /sheets/2019-12-02_5_years_since_launch.mdx: -------------------------------------------------------------------------------- 1 | # 5 Years Since Launch 2 | 3 | This week I realized it’s now been 5 full years since I [launched](https://www.producthunt.com/posts/noodles) [Noodles](https://getnoodl.es), my first app (it’s a personal recipe library). That’s crazy—I’m only a first-year in college—but I launched it the week of Thanksgiving in 8th grade[^1], after working on it for around 6 months. 4 | 5 | [^1]: Sidenote: don’t launch your app the day before Thanksgiving. No one notices. 6 | 7 | I recently stumbled across the delightful website [GitHub Contributions Chart Generator](https://github-contributions.now.sh), which does what it says on the label. I’ve always loved the way GitHub’s contributions graph works, & now I can see the whole thing at once! 8 | 9 | (The contribution graph is divisive for encouraging continuous streaks that lead to burnout, and that’s a valid criticism, but I think it can be used a fun visualization without assigning judgement.) 10 | 11 | I decided to annotate my own contributions graph, overlaying it with a timeline of life events. (I apologize this isn’t accessible to screenreaders.) 12 | 13 | ![Contributions graph with my annotations overlaid in red](https://d2wkqk610zk1ag.cloudfront.net/items/0S0t2q0s3R2A0C2N3138/5nfmtbptfmuhv92ybdm474g6wk23a0qa.png) 14 | -------------------------------------------------------------------------------- /sheets/2019-12-13_music_roundup_mid_december_2019.mdx: -------------------------------------------------------------------------------- 1 | import Embed from 'react-song-embed' 2 | 3 | # Music roundup, mid December 2019 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /sheets/2020-01-16_music_roundup_mid_january_2020.mdx: -------------------------------------------------------------------------------- 1 | import Embed from 'react-song-embed' 2 | 3 | # Music roundup, mid January 2020 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /sheets/2020-01-26_tracking_twitter_opens_with_shortcuts.mdx: -------------------------------------------------------------------------------- 1 | # Tracking Twitter Opens with Shortcuts 2 | 3 | Sometimes I use Twitter too much—especially on my phone, where I often open the app out of habit. I saw a fun idea [on Twitter](https://twitter.com/jordanmorgan10/status/1216063785812791296?s=21) (where else…) for increasing my awareness of when I open the app. I’ve been living with it for a week, and it’s wild how often I didn’t even _realize_ I was opening Twitter. Now, when I open the app, I get a notification with the number of times I’ve already opened it today: 4 | 5 | ![Preview of this automation running](https://d2wkqk610zk1ag.cloudfront.net/items/1g1c1y1M0r1G1Y1k3E3w/688jq5bbk9qt0gnhc5f6ehae4eb9rvgz.png) 6 | 7 | You can set this up with the Shortcuts app built into iOS/iPadOS 13. I’ve tried to write this article for complete beginners. You can also adapt this for any other app (like Instagram)—none of it is Twitter-specific. 8 | 9 | *** 10 | 11 | By default, Shortcuts doesn’t give you an (easy) way to save data & retrieve it later. [Toolbox Pro](https://toolboxpro.app/) is an incredible app that extends native Shortcuts functionality with nearly 100 extra actions (triggering Face ID, detecting objects in images, lots more). One of Toolbox Pro’s free actions, “Global Variables,” makes it easy to keep track of data. Before getting started, [download Toolbox Pro](https://apps.apple.com/us/app/toolbox-pro-for-shortcuts/id1476205977?uo=4). 12 | 13 | First, in Toolbox Pro, we need to create two Global Variables. You can call them whatever you want, but I used `tw_date` & `tw_count` for Twitter. In the first, write today’s date, formatted as `YYYY-MM-DD` (such as `2020-01-26`). In the second variable, `tw_count`, enter `0` (this stores the number of opens). 14 | 15 | ![Creating global variables with Toolbox Pro](https://d2wkqk610zk1ag.cloudfront.net/items/1J2R1M2C1h0k0U1H1G17/1q79vrpt7hua99u8qgf44d1v06n6fmrt.png) 16 | 17 | Next, create the automation. Open the Shortcuts app, go to the “Automation” tab, then tap “Create Personal Automation.” Select “Open App” as the trigger (bottom of the list), then select Twitter (or whatever app you want to track). Now you should be at a screen called “Actions.” 18 | 19 | To find & add an action, tap the search bar, search the name (“Global Variables,” “Format Date”, “If,” etc), then tap the action to add it to your shortcut. You can drag & drop to reorganize, & if you add the wrong action, tap the X & search again. 20 | 21 | Here’s the exact actions you want to replicate: 22 | 23 | ![Shortcut steps](https://d2wkqk610zk1ag.cloudfront.net/items/3q0a0m3I1a3h2f2y1W2j/zn20rvyakybzbejxp76qpg4zqzq4utdd.png) 24 | 25 | Whenever you need to connect actions (filling in the “If” value, etc), tap the blue fields, then tap “Magic Variable,” & pick the blue bubble with the matching name. 26 | 27 | When you’re done, tap “Next,” then _disable_ “Ask Before Running” (so it automatically runs the counter without asking you first), & tap “Done.” 28 | 29 | Open Twitter/your app, & you’ve got your first open! 30 | 31 | I don’t find blocking apps or shaming myself very effective, personally. I really like this setup because it gives me a second to pause & decide if I really want to be looking at Twitter one more time each day. 32 | -------------------------------------------------------------------------------- /sheets/2020-01-28_burned_out_on_product_hunt.mdx: -------------------------------------------------------------------------------- 1 | # Burned Out on Product Hunt 2 | 3 | I got burned out on [Product Hunt](https://www.producthunt.com) awhile ago. I want to explain why. 4 | 5 | At the beginning, there was an amazing community. I saw faces I recognized (from Twitter) & people were talking about cool products. They were gems you couldn’t find anywhere else—most didn’t have tech press coverage (yet) & were early-stage. It was incredibly cool, & that community was a distinct part of [my identity](https://twitter.com/rrhoover/status/497898074425921536?s=21) (2013-15 era). The belonging I felt there—which extended into [meeting people](https://twitter.com/nbashaw/status/549292769332985856?s=21) & [working](https://twitter.com/zackshapiro/status/495966919447429120?s=21) on [projects](https://twitter.com/zackshapiro/status/800699673325338624?s=21) [together](https://twitter.com/zackshapiro/status/800728258564288513?s=21)—is really what made tech a core part of how I present myself. I vividly remember listening to the podcast, joining the AMAs, excitedly refreshing the app. 6 | 7 | A few products I found on Product Hunt _significantly_ altered the course of my life in amazing ways. First, [Assembly](https://www.producthunt.com/posts/assembly), the community where I really learned to code. Then, [Hack Club](https://www.producthunt.com/posts/hack-club), probably the most foundational experience of high school for me. I am so, so grateful for finding these communities. (Sidenote: the serendipity of it all is crazy! One tap…) 8 | 9 | But now the site’s community feels like a ghost town. The products leaderboard feels like a list view of TechCrunch, with predictable products often from big names, mixed with ads & low-quality, easy-upvote crap. I receive DMs on Product Hunt somewhat frequently—but _every single one_ is spam, from people asking me to post or upvote their products. The notifications feel like Facebook—the majority have nothing nothing to do with me, they’re just things for me to click on. 10 | 11 | As a creator, it’s demotivating. I don’t launch products there anymore, because my last few launches were so disappointing. A huge number of the upvotes on the top products those days came from bots & spam accounts, clearly not actual humans, & Product Hunt seems uninterested in changing this. If my projects didn’t resonate with the community, that’s fine. But when the upvotes themselves are fake, it’s not worth even posting. 12 | 13 | I know a bunch of people who’ve worked at Product Hunt, & they’re all great. I try to avoid being so negative online, & I write this as an honest reflection, not at all to devalue their work. It just feels like, at some point, the company stopped optimizing for the community I used to identify so heavily with. Product Hunt used to shine so brightly when it was a community of creators & people who loved products all geeking out together. 14 | 15 | The beta of the team’s new project, [YourStack](https://yourstack.com), went public today. It’s a great way to expand for them & I’m truly excited to see where it goes. I just hope it doesn’t become like Product Hunt. 16 | -------------------------------------------------------------------------------- /sheets/2020-01-29_music_roundup_late_january_2020.mdx: -------------------------------------------------------------------------------- 1 | import Embed from 'react-song-embed' 2 | 3 | # Music roundup, late January 2020 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /sheets/2020-02-09_music_roundup_early_february_2020.mdx: -------------------------------------------------------------------------------- 1 | import Embed from 'react-song-embed' 2 | 3 | # Music roundup, early February 2020 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | ## Oh Wonder 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /sheets/2020-02-27_designing_with_color.mdx: -------------------------------------------------------------------------------- 1 | import { Columns, YouTube } from '../components/blocks' 2 | 3 | # Designing with color 4 | 5 | _(Recently someone in the [Hack Club Slack](https://hackclub.com/community/) asked me about picking colors. Thought I’d write about it!)_ 6 | 7 | Picking colors for a website, UI, logo, etc is intimidating, and [really hard](https://twitter.com/colmtuite/status/933186215800553473?s=21). I don’t think I’m very good at it either. Nonetheless, here’s some advice! 8 | 9 | ## Using color 10 | 11 | My top advice: **keep it simple**. Similar to fonts, the more colors you’re using, the harder it is to use them well. If you’re only using black and white, it’s hard to use them wrong—you only have two options for each element, and one is already the background. If you add a cyan for accents, you can selectively pick a few elements (like buttons or links) to highlight. Once you’ve added a green, orange, and pink to that mix, you’re juggling a ton. 🤹 Juggling is hard. 12 | 13 | To help keep your color usage consistent/simple in a project, use a unified palette. If you’re coding/in the browser, [CSS Custom Properties](https://developer.mozilla.org/en-US/docs/Web/CSS/--*) can be useful for this. If you’re in a design tool like Figma, I like making a grid of squares off to one side with all the colors, then never using a color not from that grid: 14 | 15 | ![Color palette in the Windy City Hacks Figma doc](https://d1fmxjrxw87eps.cloudfront.net/2020-02-27T23:22:37-05:00.png) 16 | 17 | Color theory is super complicated. I don’t have a great understanding of it, but the chapter from [*Design for Hackers*](https://smile.amazon.com/Design-Hackers-Reverse-Engineering-Beauty/dp/1119998956) was super useful. 18 | 19 | Thinking about color for an entire organization is a very different task, but [Diana Mounter](https://broccolini.net/) is amazing: 20 | 21 | 22 | 23 | (If you want more of this, look for resources on [“design tokens.”](https://css-tricks.com/what-are-design-tokens/)) 24 | 25 | ## Picking colors 26 | 27 | I’ll let you in on a secret: I rarely use colors I picked on a colorpicker. I get most of my colors from websites I like (I collect colors I like—I literally have an Apple Note called “blue” with a list of hex codes.) Having some colors saved is super handy when you need to design something in a pinch. To get colors, you can use browser DevTools if you have them (on iOS, I recommend [Inspect Browser](https://apps.apple.com/us/app/inspect-browser/id1203594958?uo=4)) or [CSS Stats](https://cssstats.com). I also frequently copy color palettes from project to project—the color palette I used for [Windy City Hacks 2019](http://2019.windyhacks.com) was all the secondary colors from [Hack Pennsylvania](https://2019.hackpenn.com). 28 | 29 | If you’re making a site/graphic around one image, or a background image, one trick I use all the time is to use the eyedropper tool in Figma/Photoshop/etc & use a color(s) from the photo as a base for your color palette—it makes the whole project look magically coordinated. [Brian Lovin](https://brianlovin.com) made [a Figma plugin](https://www.figma.com/community/plugin/744725347356614754/Dominant-Color-Toolkit-🎨) to automate this, too. Just a few days ago, I was making some social media graphics/posters (for [Queer Union @ NYU](https://www.instagram.com/p/B9CHX_FBCPm/))—notice the color matching: 30 | 31 | ![Screenshot of making color-coordinated graphics in Figma](https://d1fmxjrxw87eps.cloudfront.net/2020-02-27T23:48:54-05:00.jpeg) 32 | 33 | If you’re looking to spice things up, music videos are sometimes a fun source. There are a zillion cool color palettes just in these (excellent) music videos: 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | If you’re picking colors from a video or a still image, sometimes they’re kind of dark/desaturated. Set your design tool to HSL color, & keep the hue the same, but increase the saturation &/or lightness. Will still look like the same color but you can get a more cheery or moody palette, as desired. 44 | 45 | One other important thing to remember when picking colors: accessibility. This is a great site to check if your text/background color are accessible: [colorable.jxnblk.com](https://colorable.jxnblk.com). 46 | 47 | ## TL;DR 48 | 49 | - Color is just hard. & very subjective. 50 | - Keep it simple. The fewer colors you’re using, the harder it is to mess them up. 51 | - Save colors you like for future projects. 52 | - If you have relevant photography, extract colors from there. 53 | - Go hunting for colors from fun sources like music videos. 54 | - Always ensure accessibility of your color palettes. 55 | -------------------------------------------------------------------------------- /sheets/2020-03-20_building_predict_covid_with_redwood.mdx: -------------------------------------------------------------------------------- 1 | # Predict COVID: The first production [RedwoodJS](https://redwoodjs.com/) app 2 | 3 | This weekend, Zach Latta & I built the first [RedwoodJS](https://redwoodjs.com/) app in production: **Predict COVID-19**. It’s an interactive visualization of the global COVID-19 pandemic, allowing you to see the case trajectories of each country. The main takeaway: the U.S. outbreak is 10 days behind Italy, & tracking it exactly. (see pink line vs green below) 4 | 5 | [**predictcovid.netlify.app**](https://predictcovid.netlify.app/) 6 | 7 | ![Screenshot of site](https://rwjs-discourse.nyc3.cdn.digitaloceanspaces.com/optimized/1X/cc6b2e1a0a8a2f93345a476144fbfc898d1646cb_2_690x403.jpeg) 8 | 9 | ## Technical overview 10 | 11 | ### Backend 12 | 13 | * We used a SQLite database in development, then Heroku Postgres for early deployments, but switched to MySQL on AWS RDS for the final deployment 14 | * We used 3 tables: `Country` (for grouping/storing country info), `Day`, & `DailyCount` (each of the statistics for a day for a country) 15 | * We used a lambda function for the data scraper (does standard web scraping & saves to the database) 16 | * Deployment is hosted on Netlify, using Pingdom analytics to track performance after launch 17 | 18 | ### Frontend 19 | 20 | We used 3 [cells](https://redwoodjs.com/tutorial/cells) for the homepage, which dictated the general technical structure of the frontend. 21 | 22 | * `Countries` is the left sidebar, where you can pick which countries to plot & which is the default. I used basic React Hooks for state management here, nothing crazy. 23 | * `DailyCounts` is the main graph, rendered with Recharts. 24 | * `Stats` is the sparklines + stats at the bottom. We auto-detect what country you’re in & show stats for that country if it’s represented on the site. 25 | 26 | It’s entirely open source, here: [github.com/lachlanjc/predictcovid](https://github.com/lachlanjc/predictcovid) 27 | 28 | Shoutout to the entire Redwood team for their incredible assistance in deploying the project, including setting up the database \<3 29 | 30 | *** 31 | 32 | ## Thoughts on Redwood 33 | 34 | Redwood had some on-ramp for sure, mostly since I wasn’t previously familiar with GraphQL, but I got the hang of things pretty quickly. Deployment was the main pain point—I set up a Heroku Postgres database, but we weren’t sure if it could handle the traffic, so we switched it to Amazon RDS, & that was rough for sure. We used Netlify for the site since Redwood has built-in support/setup & Netlify gave us an unlimited plan (including Analytics) since it’s a COVID project. 35 | 36 | I 100% see Redwood becoming way bigger. I’m going to continue to use [Next.js](https://nextjs.org/) for the majority of my projects, especially since I’m not making “apps,” but whenever I need to store data, I’d use Redwood again. Its idea of cells is a great new conception, the fullstack integration is great, they help with a lot of things like forms & database queries & have CLI generators throughout the experience. It has 2 main weak points, I think: 37 | 38 | 1. It’s all client-side rendering, which is a real downside & Next stands head + shoulders above, at least for now. 39 | 2. Deployment, because you have to set up a hosted database & connect it to your main app. 40 | 41 | Server rendering they’re clearly thinking about, & considering Tom is an investor in Netlify, I wouldn’t be shocked if deployment becomes much more streamlined. Redwood won’t replace Next, but I think it makes building entire classes of applications enormously more accessible in a truly exciting way. It’s incredibly early on right now & a ton of things need a ton more work, but they’ve got several full-time developers working on it every day so I expect quick iteration. 42 | -------------------------------------------------------------------------------- /sheets/2020-03-28_music_roundup_late_march_2020.mdx: -------------------------------------------------------------------------------- 1 | import Embed from 'react-song-embed' 2 | 3 | # Music roundup, late March 2020 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /sheets/2020-03-29_going_back_to_normal.mdx: -------------------------------------------------------------------------------- 1 | # Going Back to Normal 2 | 3 | I keep hearing, from all kinds of people, “when this is over, I can’t wait to go back to normal.” 4 | 5 | This is a deeply distressing time, & I understand the sentiment. I miss so many friends, I’m disappointed about all the events I can’t attend, I miss living in NYC & my daily routine—surely after this, I can just go back? 6 | 7 | I don’t think so, but I also hope not. 8 | 9 | It’s been wild to realize as I’ve gotten older that I have been growing up in a world in crisis. In the US, supposedly the most advanced country, it’s hard to think of a system that’s _not in crisis_. From abortion access to zoos, climate collapse to dairy farming to education to forest fires to gun control, foster care to garbage collection to hospital bills to immigration to the justice system, it seems like every system around us has been in a varying degree of panic, breakdown, shambles, inequity, & non-sustainability for as long as I can remember. 10 | 11 | When we “go back” to the world, it seems more likely we’re returning to a worse version of our former world. Millions dead around the world, a huge percentage of small businesses & restaurants gone forever, cultural trauma, health insurance executives even richer. Access to abortion isn’t going to be fixed when we go back, it’ll be even tougher after pregnant people have been restricted from accessing it for months. But eventually we will “go back,” & we should build that world to be better, or more okay, as much we can. 12 | 13 | Critically, **normal wasn’t working**. Normal was destroying the habitat we live in, normal was abusing poor people & immigrants, normal was an unjust system with an expiration date. Normal was most Americans not enjoying their livelihoods, normal was most students unengaged in classrooms, normal was sending young Black men to prison over nothing. **It was only okay because it was normal, not because it was okay.** Sure, (the richer) segment of the population was able to enjoy their lives, but the average American satisfaction with life was an embarrassment for the richest country in the history of the world. We could be doing so much better. We have the resources to be happy & healthy, just put in all the wrong places. 14 | 15 | This is a reset, a moment for (the more privileged) of us to step back from the cacophony of daily life. (Though we’re also dealing with the stress & anxiety of being in a crisis & waking up every day to more deaths, higher chart-topping stats, more family members infected, more economic uncertainty, etc.) When we see our former society in the mirror, much of it is showing its rotting core. Normal was prison labor, normal was inhibited carbon emissions, normal was toxic air for poor people, normal was manipulating benefits away from gig workers. These are all entirely solvable problems, but they weren’t being solved effectively. 16 | 17 | **We can’t return to normal, nor should we.** Normal wasn’t working, even if this temporary space is worse. Everything, from a deep level, is long overdue for systematic rethinking & rebuilding. When the shuttered doors reopen, that’s the space we’ll return to: one of deep, if inequitable, re-evaluation. U.S. Bank executives will be fine, but the rest of us will be forced to re-examine every facet of our lives as we rebuild them. It’s my greatest hope we build something better. 18 | 19 | TLDR: https://microblog.hankchizljaw.com/1585480412/ 20 | -------------------------------------------------------------------------------- /sheets/2020-04-05_music_roundup_early_april_2020.mdx: -------------------------------------------------------------------------------- 1 | import Embed from 'react-song-embed' 2 | 3 | # Music roundup, early April 2020 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /sheets/2020-04-13_music_roundup_mid_april_2020.mdx: -------------------------------------------------------------------------------- 1 | import Embed from 'react-song-embed' 2 | 3 | # Music roundup, mid April 2020 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /sheets/2020-04-21_the_story_of_covid.mdx: -------------------------------------------------------------------------------- 1 | # The Story of COVID 2 | 3 | > Every story of epidemic is a story of illiteracy, language made powerless, man made brute…But, then, the existence of books, no matter how grim the tale, is itself a sign, evidence that humanity endures, in the very contagion of reading…And yet [reading] is also—in its bidden intimacy, an intimacy in all other ways banned in times of plague—an antidote, proven, unfailing, and exquisite. 4 | > —[“What Our Contagion Fables Are Really About”](https://www.newyorker.com/magazine/2020/03/30/what-our-contagion-fables-are-really-about) 5 | 6 | Through the darkest hours, humans are able not only to survive, but to continue unendingly recording the world and making creative interpretations of it. 7 | 8 | This crisis feels very different than previous pandemics must have, though. When we’re not all running around going to one thing after another all day, we’re discovering, we’re slowing down. For some people, that’s spending time with their families or doing deep work. For others, there’s grieving, there’s abusive relationships, there’s homelessness. For everyone, there’s deep uncertainty and so many questions about so many futures. But the planes aren’t flying, the cars aren’t driving. And so we turn our attention to the internet, where it feels like a type of world “town square” that could only exist in 2020: musicians and authors and drag queens and actors doing free shows from home, an overabundance of Netflix & Apple Music & reality TV & news to consume, the flourishing of grassroots media in TikToks & tweets & memes, even accompanied by astroturf white COVID truthers screaming in the corner. Ignoring that last category, it feels like we’re all in this together, somehow more connected than we ever were in our former age. At the same time, the amount of (entirely preventable) suffering & pain in the world right now is difficult to fathom. And crises like climate collapse loom in the background, continuing even when we’re not paying attention to them. The world, when we return to it, will be in a drastically different state. 9 | 10 | There will undoubtedly be many books published about this crisis—but this time, it feels almost like the books won’t be the definitive record of what happened. The memories will fade, and ultimately everything on the internet is fleeting & temporary, but the music we made & YouTube videos we uploaded & the desperate tweets & the absolute flood of media of all kinds will be this collective trail of how we got through everything. Right now, we’re just trying to get through this chaotic, stressful time. But the question is emerging: How will we tell our story this time? Whose quarantine video diary will make the documentary in 2040? Which album of bedroom-produced pop music will feel like our collective exhale? What blog posts will live on as the zeitgeist of 2020? 11 | 12 | I think the moment calls—to the privileged among us who are safe—us to get out our keyboards and pencils and guitars and paint palettes and drum machines and poetry notebooks and cameras, and record how we’re feeling, what we’re doing, how the world is handling this crisis. To capture, but also to create, to synthesize, to understand what’s going on. In the past, books have unfailingly been humans’ records. Now, let’s make our own for the internet age. 13 | -------------------------------------------------------------------------------- /sheets/2020-05-01_covid_art.mdx: -------------------------------------------------------------------------------- 1 | # COVID Art 2 | 3 | Especially in New York City, the AIDS epidemic is a natural comparison to draw to COVID—the last time people were going to their friends’ funerals every week, complete with an acronym to which there will be memorials & uncaring politicians & scars & memories to last a lifetime. Last fall, I read a story about the magnificent artist **David Wojnarowicz** from during the crisis, & wrote about it: 4 | 5 | > When a fellow artist expressed her anxiety that her photographs were not contributing to the AIDS resistance, Wojnarowicz told her: “These are so beautiful, and that’s what we’re fighting for. We’re being angry and complaining because we have to, but where we want to go is back to beauty. If you let go of that, we don’t have anywhere to go.” 6 | > 7 | > —[“One Day This Kid Will Talk”](https://ima.lachlanjc.me/one_day_this_kid/) 8 | 9 | It can feel overwhelming & paralyzing that there’s so little we can do to actually help people heal. It’s easy to wonder why we continue to make art during a time of crisis, to feel hopeless & dejected about it. Aren’t there more important things to be doing? 10 | 11 | But **if we let go of beauty, we don’t have anywhere to go back to**. We can’t let go of beauty. We can’t slip, lose control of our grasp, our attention to our consciousness & our reality. We must keep producing, creating, recording, synthesizing, sharing. Because… 12 | 13 | If we solved the pandemic but came back to a wasteland, would we be better off? 14 | 15 | *** 16 | 17 | > “Initially set to the same time, these identical battery-powered clocks will eventually fall out of sync, or may stop entirely. Conceived shortly after Gonzalez-Torres’s partner was diagnosed with AIDS, this work uses everyday objects to track and measure the inevitable flow of time…In 1991, Gonzalez-Torres reflected, “Time is something that scares me… or used to. This piece I made with the two clocks was the scariest thing I have ever done. I wanted to face it. I wanted those two clocks right in front of me, ticking.” 18 | > —[**“Untitled” (Perfect Lovers)**, MoMA](https://www.moma.org/collection/works/81074) 19 | 20 | ![Clocks by Felix Gonzalez-Torres](https://cdn.glitch.com/cedb9345-51b4-4b6b-8e74-c4f83c7a6085%2F44ec3690-2540-4291-a299-55af5113fbb4.image.jpeg?v=1589834818186) 21 | 22 | **Felix Gonzalez-Torres**, another artist from the AIDS epidemic, made this piece. Though created for a very different era, it resonates deeply right now. One of the most profound aspects of this piece is the uncertainty: when will they stop? When will they fall out of sync? And now, here we are again wondering: when will it stop? How long does each one have? 23 | 24 | At the beginning, when the world seemed to be crashing downhill so fast, I was wondering if we’d run out of food, and that’s clearly not the situation anymore. But the uncertainty has shifted: what’s safe? What’s safe enough? When will our clocks run out? 25 | -------------------------------------------------------------------------------- /sheets/2020-05-19_music_roundup_mid_may_2020.mdx: -------------------------------------------------------------------------------- 1 | import Embed from 'react-song-embed' 2 | 3 | # Music roundup, mid May 2020 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /sheets/2020-06-20_music_roundup_mid_june_2020.mdx: -------------------------------------------------------------------------------- 1 | import Embed from 'react-song-embed' 2 | 3 | # Music roundup, mid June 2020 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /sheets/2020-07-07_leave_of_absence.mdx: -------------------------------------------------------------------------------- 1 | # Leave of Absence 2 | 3 | I’ve been thinking this through for a few months, and I just can’t see a world where it makes sense to come back to NYU this fall. I’ll just be taking a leave, not withdrawing entirely, since I do hope I can return at some point in the future, but for now none of the scenarios add up for me. 4 | 5 | In summary, seems like my options are: 6 | 7 | 1. Attend fully remotely. Personally, I found the online courses this spring pretty wholly disappointing & I didn’t feel like I left them having learned much of anything or having derived real value. While of course it caught us off guard this spring, I didn’t see the university helping professors to make their online classes excel, & the results I found disengaging at best. It also…still cost full shot ($80k)…and will this fall. 8 | 2. Attend via [Go Local](https://www.nyu.edu/academics/studying-abroad/go-local.html). Seems to entail everything bad about living in a city, everything bad about taking classes remotely, none of the IMA people/facilities to benefit from, plus living in an expensive city I don’t really want to live in. Makes sense if you don’t live close to NYC, but that’s not me. 9 | 3. Take just a few credits/take the spread-out option. Reduces my motivation/ability to fully commit to my academics (already a primary concern of remote) by it not being full-time, if just a few credits then not getting financial aid if I’m not a full student, while also restricting me from having a full-time job outside of classes. 10 | 4. Come to the city. NYU’s communications thus far have been pretty non-informative, but none have addressed my primary concerns (transit/taking the subway, dining halls, what restricted social life will look like/do to my mental health), and even in the best-case scenario, I’m not hanging out with friends & building projects on the IMA floor, which are some of the main things I came to college for, or really exploring & enjoying the city. 11 | 12 | Unfortunately, if it still costs the same but provides maybe a quarter of the value while also featuring the choice between living at home or risking my health in NYC, it just doesn’t make sense for me. (The alternative, working, I can do safely wherever & comes with a paycheck, so…that’s what I’m doing. I’ve re-joined [Hack Club](https://hackclub.com/) & I’ve been having a blast designing & coding & building our community.) 13 | 14 | My professors at NYU were fantastic, and made [IMA](https://itp.nyu.edu/ima/) a joy to be apart of. This is not a turn I anticipated in my college career, & I really hope I can return when it makes sense, as I will miss the environment at NYU/IMA greatly. But in the meantime, if I can work remotely, not get COVID, earn my tuition in salary, and have an exciting, productive year, the choice seems clear. 15 | -------------------------------------------------------------------------------- /sheets/2020-07-19_music_roundup_mid_july_2020.mdx: -------------------------------------------------------------------------------- 1 | import Embed from 'react-song-embed' 2 | 3 | # Music roundup, mid July 2020 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /sheets/2020-08-01_online_hackathons_usually_suck.mdx: -------------------------------------------------------------------------------- 1 | # Online hackathons usually suck 2 | 3 | I’ve been to dozens of hackathons as an attendee, judge, & mentor, [organized](https://hackhappyvalley.com/recap) [several](https://hackpenn.com/) [events](https://2019.windyhacks.com/), & previously [written about organizing](/2020-01-19_how_to_start_your_first_hackathon/). Since COVID hit, there’s been an [explosion](https://hackathons.hackclub.com/) of online hackathons. But I’ve been skipping nearly all of them. 4 | 5 | The reality is, most online hackathons just aren’t very good. 6 | 7 | Organizing an in-person event is really hard—if you want to do it at any scale or quality, it takes dozens, hundreds, thousands of hours from organizers who really need to be invested for it to get off the ground. An online hackathon can be as simple as making a quick website, a Slack workspace, a Devpost, organizing 3 Zoom calls & calling it a day. While it’s great that more folks can start organizing, the much lower barrier to bringing a hackathon to fruition, without nearly the same challenges of sponsorship & logistics, means many are less inspired. 8 | 9 | I think it’s also way clearer what components you really need to nail for a great in-person event—good food, which requires plenty of money, a nice space, diverse attendees, workshops, swag, judging, whatever. Online, it’s a lot less clear what to focus on, & how to make those areas great. How do you get people actually talking in a Slack, without people naturally talking to who’s next to them? How do you get them to walk away with real friendships? Those don’t have easy answers like “Panera catering.” (Which I highly recommend.) 10 | 11 | Crucially, the best experiences in person (like eating ice cream with a stranger at 2am while helping each other with React.js) don’t always translate to the best experiences online. At the same time, the format for a truly compelling online event isn’t super figured out, much less standardized. We’re certainly learning that at [Hack Club](https://hackclub.com/)—[Scrapbook](https://scrapbook.hackclub.com/) has been way more successful than a hackathon we could’ve run, while in-person it wouldn’t even make sense. 12 | 13 | We’ll see where the space goes, but I think the only way forward is iteration. We need lots of events to try lots of experiments on what works amazingly online, instead of trying to directly upload a uniquely physical experience to the internet. Some components will fail, others will start to become normalized. But hackathons can be magical, life-shaping experiences—let’s figure out how to bring that ✨ onto the internet. 14 | -------------------------------------------------------------------------------- /sheets/2020-08-08_the_problem_with_online_events.mdx: -------------------------------------------------------------------------------- 1 | # The Problem with Online Events 2 | 3 | _(A follow-up to [“Online hackathons usually suck”](/2020-08-01_online_hackathons_usually_suck/).)_ 4 | 5 | Part of why online hackathons usually suck is the same reason most online translations of physical events suck: the online version is a less engaging distillation of only _the core essence_ of an event, not a reimagining of _the whole experience_. 6 | 7 | A class is distilled into only the words of the teacher, without the seeing/talking to friends, being in a different place, engaging physically. A conference is reduced to the words of the speakers, which are usually of mixed quality, with none of the being in another city, meeting people for dinner, staying somewhere novel. A hackathon is distilled into only the opening ceremony & the prizes, with none of the staying up with strangers, having conversations with people you’ll never see again, food at all hours, learning something new from the team next to you. The hackathon becomes the same thing you’re doing at home (sitting at your desk hacking), but with prizes, & the rest of it is usually boring Zooms, which we’re all tired of anyway at this point. 8 | 9 | This is part of why so many online events feel so dead: they’re worse incarnations of only the core essence, without considering everything else that’s part of the experience. To solve this, we need to consider more than simply uploading the core experience. 10 | 11 | --- 12 | 13 | I met my closest friends at NYU all during the first few days, what they dubbed “Welcome Week,” during which several hundred events happen across campus in an effort to get new students to meet one another & explore. This year? Welcome Week is just canceled. 14 | 15 | They’re reducing the entire college experience to simply the classes you take, and the classes you take to simply the words the teacher says. In the grand scheme of what college is, that’s only a small chunk, and usually a minority of the formative life experiences. (Relatedly, I’m [taking a leave of absence](/2020-07-07_leave_of_absence/).) 16 | 17 | What NYU should have spent the summer doing is experimenting, iterating, & exploring what online school could be: How can we reimagine club meetings on Zoom? How do we remake the feeling of walking around buildings & between events? What would a new social network look like made for these students in this moment? What makes a really great online class, and how do we help professors teach those? Instead, they seem to be busy trying to get a portion of students back onto campus, for whatever limited version of the college experience is possible amid the chaos. It’s not unexpected, but it is disappointing. Schools with the creativity, talent, money, and resources like NYU should be paving the way, but instead they’re stuck in the default tracks of prior wisdom. 18 | 19 | --- 20 | 21 | I think we still haven’t _really_ figured out what amazing, online-native events look like. Certain instances stick out: HQ Trivia, in its heyday, attracted massive crowds to a real-time, mobile-native experience. I’m very disconnected from it, but Fortnite seems to manage it. Livestreams of big cultural moments, like the recent SpaceX astronaut launch, are watched by many, but that’s more of a TV experience than internet. 22 | 23 | Interestingly, after all the efforts of YouTube, Facebook, Instagram, Twitch, Twitter/Periscope, Microsoft/Mixer, and other companies have poured into livestreaming, livestreams are still a small fraction of the time I & most people spend watching video. There’s reasons for this; there’s a much lower quality ceiling, it’s difficult to produce, it’s incredibly difficult to make engaging outside of specific genres (gaming, broadcasts of calls like [AMAs](http://hackclub.com/amas/)). But it doesn’t make any sense that synchronous, life-defining events rarely happen on the most remarkable distributed, accessible, real-time, interactive platform humans have ever had, the internet. 24 | 25 | To really answer the question of online events, we need to figure out how to not just upload the core essence of an event, but reinvent all parts of it to be native to the internet. Schools like NYU should be doing it, companies should be doing it, communities like [Hack Club](https://hackclub.com/) are [trying](https://events.hackclub.com). I can’t wait to attend an online event that feels really real, and feels better online than it would have in-person. There’s so much potential & not nearly enough iteration & experimentation right now. 26 | -------------------------------------------------------------------------------- /sheets/2020-09-14_music_roundup_september_2020.mdx: -------------------------------------------------------------------------------- 1 | import Embed from 'react-music-embed' 2 | 3 | # Music roundup, September 2020 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /sheets/2020-10-17_music_roundup_october_2020.mdx: -------------------------------------------------------------------------------- 1 | import Embed from 'react-music-embed' 2 | 3 | # Music roundup, mid October 2020 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /sheets/2020-11-12_i_quit_hack_club.mdx: -------------------------------------------------------------------------------- 1 | # I quit from Hack Club 2 | 3 | _This is the message I posted in the [Hack Club Slack](https://hackclub.com/slack/), November 12, 2020._ 4 | 5 | Hi friends 👋 6 | 7 | September 2017, over 3 years ago, I was at PennApps (my first hackathon), pacing around, on the phone with Zach Latta in the middle of the night to get (super useful) advice on my hackathon project. He mentioned he wanted to hire me to make a new website for Hack Club, an offer I was humbled & surprised by but took him up on soon after hackathon-recovery. We spent the rest of the fall on frequent calls working on drafts of a new hackclub.com (https://web.archive.org/web/20171221102322/https://hackclub.com/), which pushed my skills hard. 8 | 9 | Over the last 3 years, I’ve worked on our website, designed several generations of Bank, made Scrapbook, the Hackathons site, Workshops site & curriculum, Events site, the Summer of Making, designed stickers, co-authored every VIP newsletter, made Hack Club Design System, Theme, Icons, branding, open source projects, AMAs, Challenge, holiday gifts, Shop, donor materials, a Flagship talk, Leaders, student announcements, social media, iPad distribution, run events, hackathons, Slack sub-communities, & lots more I can’t remember anymore. I’ve poured my heart & soul into Hack Club, & I feel deeply proud of what I’ve gotten to help build. It’s been a privilege to travel to San Francisco, LA, Chicago, NYC, Vermont, Indiana, & spend so much time with every member of the incredible team. Hack Club’s mission—helping young people discover their agency & build skills—is the cause I believe in the most. In the Slack, I’ve found many of my closest friends. 10 | 11 | Tomorrow is my last day at Hack Club HQ, I’m sad to announce. I’ve been working on Hack Club for 3 years, & it’s the only real job I’ve ever had. My next adventures are super WIP, but for a lot of reasons it feels right to take a new step. I’ll still be around the Slack, & here to chat with anyone. Thank you all for making this place so incredible, & to Zach & the team for this opportunity. Keep hacking 💖 12 | 13 | —@lachlanjc 14 | -------------------------------------------------------------------------------- /sheets/2020-11-17_music_roundup_november_2020.mdx: -------------------------------------------------------------------------------- 1 | import Embed from 'react-music-embed' 2 | 3 | # Music roundup, mid November 2020 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /sheets/2020-12-09_take_advantage_of_intern_impostor_syndrome.mdx: -------------------------------------------------------------------------------- 1 | # Take advantage of intern impostor syndrome 2 | 3 | Experiencing impostor syndrome is often the nature of internships—you end up feeling unprepared because, well, everyone around you has been doing this work for years. If you already knew how to do everything & were experienced doing it, you’d have been hired full-time already—the whole point of an internship is to learn & grow in a real, professional environment. 4 | 5 | I got my first internship shortly after building my first app, at a company called Highrise than spun out of Basecamp (makers of HEY). I was working primarily with someone I super looked up to & someone who’d just sold his startup to Dropbox. All my coworkers were incredibly smart & good at their jobs, & I was dropped into this company on a team of 4-5 where I had no idea how to use any of our (admittedly outdated) tools but presumed I was a genius who could solve it all. They were incredibly kind & mentored me—I ended up making some solid contributions by the end of the summer, but I was spectacularly unprepared for the job. 6 | 7 | Learning how to use Rails 2.0 & edit a late-2000s codebase was actually probably my least important takeaway from that summer. I got paid for my work coding, which I think is a really formative experience, I learned how to work with teammates respectfully, which is a long but critical journey, & I left with supporters & professional relationships I still maintain. 8 | 9 | If you’re starting a new internship, I would focus on that last part: building relationships with coworkers, where they’ll mentor you because they enjoy it & you really grow a connection that lasts past the summer’s end. Those relationships often end up being more meaningful than whatever technology you learn. Coworkers are surely impressed with everything you can already do—many will probably be thrilled to work with you 1-on-1 a lot, so take advantage of that. 10 | 11 | Listen, a lot, & seek out learning from folks at the company. Judge a lot less how much you can get done every day, & a lot more on leaving the summer proud of having learned a lot, met some great people you can keep in touch with, & having made a few awesome things with folks. Impostor syndrome is natural—but if you can turn technical impostor syndrome into a reason to get to know the people around you, you can walk away with a lot more than new technical knowledge. 12 | -------------------------------------------------------------------------------- /sheets/2020-12-16_music_roundup_december_2020.mdx: -------------------------------------------------------------------------------- 1 | import Embed from 'react-song-embed' 2 | import Music from 'react-music-embed' 3 | 4 | # Music roundup, December 2020 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | ## Bonus: Christmastime playlist 17 | 18 | 19 | -------------------------------------------------------------------------------- /sheets/2020-12-25_production_value_changes_the_product.mdx: -------------------------------------------------------------------------------- 1 | # Production value changes the product 2 | 3 | I have a tendency toward ever-higher production value. As a creative person, this is often a natural draw over time. 4 | 5 | On [episode 104 of Cortex podcast](https://overcast.fm/+E7b7wF8gs/55:58), CGP Grey discussed a similar situation. For him, it wasn’t merely a personal/business move, but also a business one—his Patreon charged patrons only when he released a video, so he hesitated to release anything smaller than what’s come before for fear of disappointing them. But over time, it stifles creativity & limits possibilities to have this ever-rising standard. 6 | 7 | Beyond a simple webcam vlog, YouTube videos of any production value are essentially always for an audience. You don’t buy lights & cameras or do animation, write scripts, record takes, edit & export & publish a complex video as a note to yourself. In contrast, blogs can seamlessly blur the line of being for yourself or an audience—you can take a note jotted on a phone & make it public in a few seconds. ([Here’s the shortcut](https://www.icloud.com/shortcuts/61f5b09ca3744f14a11e483fb1bc9be3) I use to publish with, using [Working Copy](https://workingcopy.app/).) 8 | 9 | Making something at all public exists inherently for an audience, but its role in the author’s life can go far beyond that. While there’s certainly a divide between my personal notes & what I publish on my Notebook, I want it to be a space that's not entirely performative. Performative, let me clarify, is not bad—many of the most impactful things we read/consume were not dashed-off notes to self, they are carefully-written, [thoroughly-edited](https://youtu.be/vykcFkmf_IQ) pieces made over a [significant period of time](https://overcast.fm/+EwEyvtW8c). Very rarely are impromptu, one-take creations truly [eloquent](https://youtube.com/watch?v=LI4ueUtkRQ0) & powerful. 10 | 11 | The ease of adding, publishing, & updating is a key aspect of this. This blog runs on files stored in Git on GitHub, & while it’s not _difficult_ to make commits & push them, it takes a conscious move & has some weight to the action. One thing my friend Zach Latta did to simplify his personal notes setup, which also runs on Git/GitHub, is have a script that automatically commits & pushes his folder of notes every 5 minutes, so it becomes an unconscious, automatic move. (An iOS shortcut using Working Copy complements the Bash script.) 12 | 13 | Higher production values can be amazing, but they can also be stifling. The creators of Stranger Things having an idea & getting it onto Netflix is a gap filled by years & [$10M/episode](https://variety.com/2017/tv/news/tv-series-budgets-costs-rising-peak-tv-1202570158/). On this Notebook, it’s usually at minimum 15 minutes. In the notes app on my iPhone, it’s \<30 seconds. Those interactions & the barriers between drastically change the scope of ideas & what ends up being produced. 14 | 15 | I think it’s critical with creative projects to be explicit about the production value you’re looking for. It naturally shifts upwards over time—look at the quality of CGP Grey’s videos, the visual effects on Stranger Things, the pages of this Notebook over time. That can be amazing, & enable the creation of increasingly stunning & impactful works over time, but personally as a creative person it can also be oppressive. It can make you stop writing entirely for fear of the article not being as great as the last. 16 | 17 | So be clear with yourself when you’re creating. Maybe the pages you’re adding to a website must always be performant, typo-free, accessible, responsive, look great. Or new posts on the blog should be edited, but never take more than a day. Or a new note in this folder should happen fluidly in 30 seconds & be published automatically. Your work will be liberated by this declared constraint, instead of stifled under shifting vagueries of self-expectation. 18 | -------------------------------------------------------------------------------- /sheets/2020-12-27_a_merry_2020_christmas.mdx: -------------------------------------------------------------------------------- 1 | # A merry 2020 Christmas 2 | 3 | My family celebrates Christmas as the common, commercialized cultural holiday. It’s a time of year I very much enjoy. In recent years I’ve gotten way more into giving gifts, & I find it really gratifying. This year feels out of place: no trains, buses, flights, reuniting hugs. Christmas felt like a regular day with the addition of presents. 4 | 5 | In years prior, in high school, the period after Thanksgiving before the winter break was always the roughest for my mental health. The short days, cold mornings of sleep deprivation, stressful projects all crammed in. In 2016, the election happened, meanwhile I was trying to figure out if I really was queer, but I hadn’t told anyone. 6 | 7 | This year has honestly been the best it could’ve been. There were some enormous opportunities that slipped through the crack of this spring, & going from living on my own in NYC back to my childhood bedroom in the woods isn’t the direction I wanted to be going in at age 19. But I’m healthy, out, my family is well, I have a wonderful relationship & friends, my mental health is in a great spot. I was able to [leave my job](/2020-11-12_i_quit_hack_club/) last month for my own well-being & spend the next month working on creative & open source projects, not worrying about money. Taking a [leave of absence](/2020-07-07_leave_of_absence/) for this whole school year was the right choice for me. I feel unbelievably grateful to not even fully know how grateful I should feel. 8 | 9 | Looking to next year, I want to branch out. Most of my projects this year only pushed me in smaller ways I knew I could be pushed. There’s nothing wrong with that, & I wouldn’t change how I spent my year looking back. I didn’t learn significant new technologies or have significant new experiences I was nervous about, though. Looking at other potential jobs, new projects, going back to college & NYC for a second year, I want to expand my horizons. The year of branching out, with any luck. 10 | 11 | I’ve got one project coming out in the next few days, an oddly-timed one I’ve really been pouring my heart into recently to tie a bow on this ridiculous year. I hope you’re enjoying the holidays too. 12 | -------------------------------------------------------------------------------- /sheets/2021-01-13_music_roundup_january_2021.mdx: -------------------------------------------------------------------------------- 1 | import Embed from 'react-song-embed' 2 | 3 | # Music roundup, January 2021 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /sheets/2021-01-13_prioritizing_awareness_enables_my_focus.mdx: -------------------------------------------------------------------------------- 1 | import Song from 'react-song-embed' 2 | 3 | # Prioritizing awareness enables my focus 4 | 5 | (I wrote this listening to “Chemtrails Over the Country Club,” you can listen while you read if you’d like!) 6 | 7 | 8 | 9 | --- 10 | 11 | It’s no secret that we live amongst/despite a constant onslaught of horrendous events. The number of deaths, protests, medical emergencies, human rights violations, political fiascos, shootings, & natural disasters we live alongside is beyond comprehension. But as the years go by, I’ve increasingly, unconsciously learned to disconnect myself from what’s happening in the world. I’ve realized that otherwise, I’m incapacitating myself from doing the work I care about to help. 12 | 13 | Being able to simply ignore world events is a position of extreme privilege, one where you must feel an underlying assumption that regardless of what happens, it won’t significantly hurt you. In a world of open, mainstream hate & trans bathroom bills, that is not my position. But there is an infinite amount of hurt, pain, & trauma in the world, at an ever-increasing rate. There is no possibility of truly understanding at a personal level the pain of all the people. You can never read all the news about all the important things happening, because it would take all your time. You can never process all the suffering, because you’d never have the emotional bandwidth to process your own life & be there for people around you. Believing otherwise leads you down a rabbit hole of misery, and prevents you from doing important work in the world. 14 | 15 | So you have to focus. 16 | 17 | I tune a lot out. A huge chunk of political news is disguised to look like it affects you, but in reality it can be ignored. You can follow it out of curiosity, but it’s acting more as entertainment than affecting your life & work, & recognizing that frees you from feeling perpetually attached as it evolves. 18 | 19 | There are an unending number of worthwhile causes & places to focus one’s energy to help build a more progressive world. In my work, I largely focus on 3 efforts: climate change, LGBTQ+ liberation, & giving agency to young people. Each one of these has more than a million careers’ worth of worthwhile work to do. I don’t focus on these because racial justice, immigration reform, & economic inequality aren’t also worth vastly more attention than they’re getting—I work in a few areas because if I tried to spread my work out to all these causes, my work would never amount to anything meaningful for any of them. It’s about **prioritizing with purpose**. 20 | 21 | This doesn’t at all mean you should ignore what’s happening. We have a responsibility to use the internet & the unbelievable abundance of resources we have to be more aware/informed than people ever could be in the past. Being able to follow people from all walks of life & hear directly from them can genuinely build an empathy that media a few decades ago never could. We can support folks who are working on other problems than us—financially, sharing & elevating their work, collaborating. These are superpowers, magnifying all of our impacts, that never used to be possible. 22 | 23 | One way I’ve found more impactful & efficient to keep myself informed is prioritizing reading more retrospective, longform journalism than real-time updates. Like everyone else, I’ve read an enormous number of articles about COVID this year. The (multiple) hours I spent reading [The New Yorker’s “The Plague Year”](https://www.newyorker.com/magazine/2021/01/04/the-plague-year) recently gave me so much more insight & critical information than the sum of many small updates this year. I tend not to read a lot of non-fiction books about current events, but devoting time to longform journalism—which I read on Apple News+ now, as the quality of Medium has declined precipitously for me—has been way more useful to me. 24 | 25 | Entertainment more than serves a role here: both in traditional forms & news. Personally, I pay outsized attention to [all Apple news](/2019-09-10_why_i_care_about_apple/). It’s entertainment for me, in the same way sports are for some people: an area where there’s always something happening to distract me, largely not of consequence. Following something lacking dire consequences is one of my few escapes from this reality, & generally recommendable. 26 | 27 | As one calamity bleeds into the next, it feels bizarre to disconnect from much of our reality as the world grows increasingly chaotic. But to stay sane & keep my motivation going to work toward the world I want to build, I can’t listen to everything. It’s about focusing on the news more impactful on my work, prioritizing work on the issues I can bring meaningful contributions to, & using more news/listening to people outside those to enrich my understanding & work of the world. We have much to do. 28 | -------------------------------------------------------------------------------- /sheets/2021-01-14_ipados_14_is_the_best_digital_handwriting_yet.mdx: -------------------------------------------------------------------------------- 1 | import { Player } from '../components/blocks' 2 | 3 | # iPadOS 14 is the best digital handwriting yet 4 | 5 | I’ve [written before](/2019-09-23_digital_handwriting/) about digital handwriting. There have been hundreds of products working on the digital handwriting experience for decades, from the [Newton](https://en.m.wikipedia.org/wiki/Apple_Newton) to [reMarkable](https://remarkable.com), the promise of a digital device that can truly understand your handwriting still looms large. 6 | 7 | This is not a [comprehensive review](https://www.macstories.net/stories/ios-and-ipados-14-the-macstories-review/13/#handwritten-notes), but a written observation that the Notes app on any modern iPad with an Apple Pencil is my favorite place to handwrite with any type of stylus. The imperceptible latency (partly achieved through [iOS 9’s predictive drawing](https://www.macstories.net/stories/ios-9-review/8/#images-and-sketches)), opening space anywhere in your document, [data detectors](https://twitter.com/viticci/status/1275772230719455233?s=20), & natural selection just by moving your finger & solid OCR for sharing make it best-in-class. Their [hard work](https://www.relay.fm/connected/301) seriously paid off. 8 | 9 | The [FiftyThree Pencil](https://en.m.wikipedia.org/wiki/FiftyThree#Pencil_and_The_FiftyThree_SDK) still beats Apple Pencil at having an eraser simply on the back of the Pencil itself, instead of the slightly convoluted & difficult-to-explain double-tapping against the Pencil itself. (FiftyThree’s Pencil also had a [banging landing page](https://destroytoday.com/blog/building-the-pencil-page) that’s an obvious precursor to Apple’s [recent web design](https://www.apple.com/airpods-max/); Apple Pencil’s used to be better but never came close to FiftyThree’s. And pour one out for everything FiftyThree’s Paper was 2011–2014—it pioneered a lot of what drawing & writing on the iPad is now, and [Muse](https://apps.apple.com/us/app/muse-tool-for-thought/id1501563902?uo=4), [GoodNotes](https://apps.apple.com/us/app/goodnotes-5/id1444383602?uo=4), [Procreate](https://apps.apple.com/us/app/procreate/id425073498?uo=4), even the Apple Pencil are descendants of their remarkable work.) 10 | 11 | 12 | 13 | In addition to the Apple Pencil hardware itself, Apple’s software has its issues as well. Using a [Clear-like](https://www.theverge.com/2012/2/15/2799318/clear-for-ios-hands-on-video) pinch-to-open gesture to open space instead of the finnicky popup menu + slider would be even more natural & elegant. Being able to drag content like links & photos inline (like [Muse](https://museapp.com/handbook/)) would be even better as well. It’s difficult to read notes handwritten on iPad on iPhone since they’re [so scaled down](https://cloud-jmcceqwdo.vercel.app/2021-01-14_10m067u4tnu4aujhgxc4eev2k4bfgz3p.png). 14 | 15 | Beyond the scope of the iPad itself, we still have no good way of representing handwiting in an accessible, responsive way on the web. This post’s first draft was scribbled in iPadOS 14 Notes, converted to text, then edited & hyperlinked with Markdown/MDX because standard HTML is so drastically easier to publish & read on the web. Unfortunately, the text conversion process takes a hot second for a full document, even on recent A-series chips, & the process of cleaning up the mistakes made by the conversion is almost more frustrating than typing out the whole document fresh. 16 | 17 | Combined with [iPadOS 14 Scribble](https://support.apple.com/guide/ipad/enter-text-with-scribble-ipad355ab2a7/ipados), there’s no question we’re making big strides toward having computers that can more naturally understand our chaotic monkey body inputs. But [like voice assistants](https://www.ben-evans.com/benedictevans/2017/2/22/voice-and-the-uncanny-valley-of-ai), it sometimes feels like the closer they get & the higher our expectations become, the more continually disappointing they are that we just aren’t there with fluidly understanding human inputs like handwriting. That said, I sometimes have trouble deciphering my own handwriting, so maybe I’m just expecting too much. 18 | -------------------------------------------------------------------------------- /sheets/2021-01-15_airpods_max_are_priced_perfectly.mdx: -------------------------------------------------------------------------------- 1 | # AirPods Max are priced perfectly 2 | 3 | A lot has been made of the price of AirPods Max. Which is understandable—the price of an object is a huge part of a product’s identity & marketing, especially a new, high-profile product. $550 is certainly expensive for consumer headphones; while audiophile headphones can be several times that price, they aren’t marketing to the majority. 4 | 5 | Most of the discussion at the launch of AirPods Max seemed to utterly miss the point, I believe. [ATP](https://atp.fm/408) & [The Verge](https://www.theverge.com/22177494/apple-airpods-max-headphones-review-price-features) publish a lot of great work, but the reality is, high-end, wired audio customers aren’t coming to Apple’s AirPods brand for headphones. There’s cheaper, more sound-accurate wired headphones out there than what Apple is making. 6 | 7 | To me, the most standout feature of the AirPods Max is the striking design. One size & 5 colors is perfectly Apple for a design accessory—a little bit of choice, but entirely within Apple’s palette. They look simultaneously retro & futuristic, utterly simple but meticulously refined. Regardless of people’s many opinions, the headband & case look like nothing else on the market. 8 | 9 | Apple’s [pricing](https://www.macworld.com/article/3600247/3-rules-to-understand-the-price-of-apple-products.html) is never the budget leader. It’s a huge part of the identity of the MacBook that they’re amazing computers nearly every well-known creative person owns, as well as that they start at $999. That AirPods start at $159 (which was much-decried upon release, but now totally normal) is very much [part of their identity](https://www.urbandictionary.com/define.php?term=i%20can%20t%20hear%20broke) as well. 10 | 11 | AirPods Max were always going to be high-priced, just for the features they offer—no other headphones, due to Apple’s strategic choices, have the H1 chips with seamless pairing & device switching. Just having the Apple brand (even if [no logo](https://twitter.com/vladsavov/status/1348827785943347202?s=20)) is worth a premium. 12 | 13 | But by producing a one-of-a-kind design (which reportedly [took 4 years](https://9to5mac.com/2020/12/09/airpods-max-development/)) & pricing them at $550, Apple made clear that they’re personal technology [doubling as a fashion accessory](https://www.youtube.com/watch?v=4DLNqACv9Ug). Interestingly, it’s arguably Apple’s own Beats, a direct competitor in some ways, that started that trend in the early 2010s. 14 | 15 | As a human on a global scale, I’m very well-off, though not always compared to many of my NYU classmates/friends. I take real pride in my appearance, & my style is very important to me, though most of my clothes are under $25 from H&M & I don’t own anything designer. 16 | 17 | When it comes to headphones though, I would gladly sacrifice audio quality for style, & price for style. I simply have no interest in covering my head with black plastic. The Sony & Bose competitors AirPods Max have primarily been compared to are ugly & made of cheap-looking plastic, versus AirPods Max’s unibody aluminum, shiny stainless steel, & knit headband. The Digital Crown with a glass inlay is aesthetically (as well as an input mechanism) so preferable to an awkardly-placed USB-C port [hanging out](https://www.amazon.com/Sony-WH-1000XM4-Canceling-Headphones-phone-call/dp/B0863TXGM3). (This absolutely makes me sound like [an Apple fangirl](/2019-09-10_why_i_care_about_apple/) & general snob, but hey, we all prioritize differently.) 18 | 19 | AirPods Max are priced perfectly: the price says that they’re undoubtedly luxury tech, as well as exceeding the value they have as a technological accessory by becoming a fashion accessory in a way that “WH-1000XM4” inarguably is not. I can’t wait to try some out. 20 | -------------------------------------------------------------------------------- /sheets/2021-01-21_design_with_whitespace_and_confidence.mdx: -------------------------------------------------------------------------------- 1 | # Design with whitespace & confidence 2 | 3 | [Steve Schoger on Twitter](https://twitter.com/steveschoger/status/1351552879715344384?s=20): 4 | 5 | > One of the easiest and most overlooked ways to make a site look more “designed” is to use liberal spacing. Always start with more spacing than you think you'll need. 6 | 7 | ‪This is 100% true. I typically gravitate toward smaller spacing by long habit, but adding lots more space is almost always the way to go. Everything needs space to breathe, especially on a marketing site. 8 | 9 | Whitespace is an important part of the storytelling of a website. It can show separation, add dramatic effect, & moderate the speed at which you perceive elements by changing the amount of scrolling. Whitespace says things your words don’t. 10 | 11 | With my web design, I uncounsciously used to be petrified of boring viewers, so I tried to make every section pack a punch, then pack those together. But no matter how beautiful each component is, when all jammed together, they lose effectiveness. 12 | 13 | In spring 2019, I made a [website for Hack Camp](https://hackclub.lachlanjc.com/camp/), where I used some of the largest headings & most whitespace (or dark grey space, at least) of any of my designs to that point. When I was designing it, it at first felt like too much, but the hierarchy actually works really well. It does the opposite of boring viewers—it emphasizes what’s important, differentiating with clarity. Whitespace allows drawing focus to the content, instead of drawing focus to general busy-ness in a design. 14 | 15 | Hack Camp website screenshot 16 | 17 | There can, of course, be too much of a good thing: if there’s too much whitespace, your page can look bizarrely barren & people won’t know where there’s more content. But using generous whitespace effectively, giving elements space to breathe & clear separations where they’re logical, is one of the best ways to make your design shine. 18 | -------------------------------------------------------------------------------- /sheets/2021-01-21_fonts_on_ipad_2021_edition.mdx: -------------------------------------------------------------------------------- 1 | import { AppGrid } from '../components/blocks' 2 | 3 | # Fonts on iPad, 2021 Edition 4 | 5 | Last spring [I wrote about fonts on iPad](/2020-04-13_fonts_on_ipad/), looking at the multitude of bad options for installing custom fonts on my favorite operating system. Good news! I have an update, & it’s quick. 6 | 7 | **Just use [Fontcase](https://apps.apple.com/us/app/fontcase-manage-your-type/id1205074470?uo=4).** It has a much simpler & prettier design, it’s free, it’s made by the [Iconfactory](https://iconfactory.com) & even [open source](https://github.com/manolosavi/xFonts). 8 | 9 | ![Screenshot of Fontcase on iPad](https://res.craft.do/user/preview/ed1a5d33-35bf-4add-9806-36a24cb98e5e/doc/4C37EC18-2180-45D4-AA68-B2C64D8075F8/8F2FAE1E-78A8-40B6-8F8A-4872F1563867_1) 10 | 11 | In Fontcase, it’s easy to import fonts from Files using the “Import” button, & it’s more seamless setting up the system profile since it uses a single profile for all the fonts in Fontcase, versus individual profiles for each font like [AnyFont](https://apps.apple.com/us/app/anyfont/id821560738?uo=4). 12 | 13 | ![Screenshot of Figurative on iPad with a text layer in a custom font](https://res.craft.do/user/preview/ed1a5d33-35bf-4add-9806-36a24cb98e5e/doc/4C37EC18-2180-45D4-AA68-B2C64D8075F8/87C5F8B4-72AA-4089-9504-7A08D2BA2689_1) 14 | 15 | If you work with Figma, Fontcase works great with your Figma projects via the unbelievably-great app [Figurative](https://apps.apple.com/us/app/figurative/id1510607687?uo=4), which is somehow free. Figurative could easily charge \$20—instead of the semi-broken version in Safari, you get a fully-functioning Figma on iPad. I much prefer it to traditional computers since I can use my fingers & Apple Pencil, avoiding the RSI I get from using a trackpad/mouse. All your fonts are available, drawing works properly with the Pencil, & when you export layers it activates the native iPadOS share sheet for sending or saving to files. (If you want to save directly to a server, [Secure ShellFish](https://apps.apple.com/us/app/secure-shellfish-ssh-sftp/id1336634154?uo=4) is the way to go.) 16 | 17 | 18 | 19 | - [![Fontcase - Manage Your Type](https://is2-ssl.mzstatic.com/image/thumb/Purple123/v4/a9/81/70/a981705f-50bd-c28b-c051-235c830e1efa/source/512x512bb.png) Fontcase](https://apps.apple.com/us/app/fontcase-manage-your-type/id1205074470?uo=4) 20 | - [![Figurative](https://is5-ssl.mzstatic.com/image/thumb/Purple124/v4/ca/ef/dd/caefdde7-19bc-e759-d534-0c9720509b3c/source/512x512bb.png) Figurative for Figma](https://apps.apple.com/us/app/figurative/id1510607687?uo=4) 21 | - [![Secure ShellFish - SSH & SFTP](https://is1-ssl.mzstatic.com/image/thumb/Purple114/v4/bc/8d/3b/bc8d3b47-6c70-a96e-e654-268caa8ca84e/source/512x512bb.png) Secure ShellFish](https://apps.apple.com/us/app/secure-shellfish-ssh-sftp/id1336634154?uo=4) 22 | 23 | 24 | 25 | Fontcase is a must-have utility for designers using an iPad. I’ve added both Fontcase & Figurative to [my Tools page](/tools), & all the apps I’ve mentioned are free. 26 | -------------------------------------------------------------------------------- /sheets/2021-01-27_design_and_content_go_hand-in-hand.mdx: -------------------------------------------------------------------------------- 1 | # Design & content go hand-in-hand 2 | 3 | For truly thoughtful, effective web design, the content & the design must be composed hand-in-hand. On a great page, the design is not just communicating the words, but is supporting them & speaking for itself. This is why CSS templates always end up feeling dull: the content has been fit into a templates set of boxes, instead of building unique boxes with shared characteristics around the content. 4 | 5 | For a [recent page explaining local climate change](https://centresustains.com/climate), this especially came to fruition. I realized that even as someone who’s tried to research & understand climate change better than average, I couldn’t explain what was changing about climate in my own town. For most web projects, I need to deeply understand the topic before I can effectively communicate about it; for this project, I wanted to make the page partly to answer my own questions. I was working with [Pam Adams](https://www.linkedin.com/in/pam-adams-0ba92611), who provided all the research/knowledge & drafts of the writing. We went back-&-forth dozens of times, her writing a first draft of each section, my trying it out on the page & editing it down, her expanding with more information & wording. 6 | 7 | While most local government communications end up obscured by bureaucratic language, we aimed for intense clarity & accessibility. As I designed the page, making the content & the design speak together was my greatest challenge. There’s lots of pages with charts of carbon dioxide or lists of effects of climate change; what’s rare is making them communicate something beautifully together. 8 | 9 | [![Screenshot of the Forests section of centresustains.com/climate](https://cloud-m5f0wwwog.vercel.app/2021-01-27_screen%20shot%202021-01-27%20at%2015.27.17.png)](https://centresustains.com/climate) 10 | 11 | Making a template for this, interleaving icons with the text & highlighting part of the text like that wouldn’t have occured to me, because no words in “lorem ipsum” stand out to me. Looking at the real statistic, it’s obvious where I should draw attention to. 12 | 13 | I also always end up changing the content itself as the design takes shape: adjusting words to change line breaks, moving words so they can be highlighted together. When I’m working with a client who’s written text I can’t edit, it always makes for a worse site in the end. 14 | 15 | [Reminder: Design is still about words](https://signalvnoise.com/posts/3404-reminder-design-is-still-about-words). Drop the lorem ipsum, get some real words in there—even if those words are a draft, you’ll be able to design with a new level of clarity, with much more interesting results. 16 | -------------------------------------------------------------------------------- /sheets/2021-01-30_you_cant_be_in_complete_awe.mdx: -------------------------------------------------------------------------------- 1 | # You can’t be in complete awe 2 | 3 | > I think you’ve always got to be critical and try to imagine things that aren’t there, as well, and not be satisfied with what is currently there. If you’re in complete awe of something, it doesn’t push you to try and imagine things around it. It’s about elements that are already out there, but imagining how they could be further distilled, concentrated, intensified. 4 | > 5 | > —[SOPHIE to Rolling Stone on PC Music](https://www.rollingstone.com/music/music-news/pc-music-are-for-real-a-g-cook-and-sophie-talk-twisted-pop-58119/) ([RIP](https://twitter.com/rinasawayama/status/1355496702602588161?s=20)) 6 | 7 | I used to absolutely be in awe of certain designers. [Brent Jackson](https://jxnblk.com) for sure, & a number of others. When I would go to make my own thing, I’d pull up some of their sites & try to—though I wouldn’t admit it then—essentially remake their work. But I’m not them, & lack all their creative inspirations & context. Whatever I would make had an implicit creative maximum of another designer’s one work, which for a new project means it remains continually disappointing. 8 | 9 | > I got into design by copying others until I was comfortable enough to design it on my own. 10 | > 11 | > —[Jordan Singer on Twitter](https://twitter.com/jsngr/status/1354627216680873987?s=21) 12 | 13 | My critical realization was not only that the designers I looked up to weren’t infallible, but that the aesthetic my projects needed simply didn’t exist yet. There was nowhere I could go to follow, I had to make it. My designs are of course still filled with inspiration from the thousands of things I’ve seen & certain designers in particular, but they no longer have a creative maximum defined by one other person’s work. You can & should admire other creative people in your field—but it’s when you’re no longer in complete awe that you’re able to make truly new things. 14 | 15 | Read next: [Monsters and Thieves by Nate Kontny](https://signalvnoise.com/posts/3794-monsters-and-thieves) 16 | -------------------------------------------------------------------------------- /sheets/2021-02-27_music_roundup_february_2021.mdx: -------------------------------------------------------------------------------- 1 | import { Box, Text } from 'theme-ui' 2 | import Embed from 'react-song-embed' 3 | 4 | export const Movie = ({ href, img, name, desc }) => ( 5 | 36 | 37 | {name} 38 | 39 | 40 | {desc} 41 | 42 | 43 | ) 44 | 45 | # Music roundup, February 2021 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | -------------------------------------------------------------------------------- /sheets/2021-03-02_automate_setting_up_new_macs.mdx: -------------------------------------------------------------------------------- 1 | # Automate setting up new Macs 2 | 3 | I recently erased my MacBook Pro to set it up fresh, then soon after, got a work laptop. 4 | Setting up a new Mac, with all the tools one needs as a developer, is a nontrivial process, & ripe for automation. 5 | 6 | After being a longtime fan of the [Thoughtbot Laptop](https://github.com/thoughtbot/laptop) project to automate installation of various development tools, I wanted to customize it. I [forked the project](https://github.com/lachlanjc/laptop) to set up all the tools I use, enable hidden files on macOS, & then documented the manual steps I take as well. Since I continue to update that project, check out [its README](https://github.com/lachlanjc/laptop#README) for an up-to-date list of what it sets up, but it includes Homebrew, Git, Node, Yarn, Ruby, & a ton of other developer tools. 7 | 8 | ## My macOS setup process 9 | 10 | - During setup, skip Siri & Screen Time, enable all the other default settings 11 | - In System Preferences, [turn on DND, 5:00am–4:59am](https://twitter.com/lachlanjc/status/1355998974226870273?s=20) (then Cmd-drag DND out of the menu bar) 12 | - In the Keyboard settings: 13 | - Modifier Keys. Remap Caps Lock to Escape 14 | - Customize Control Strip. Remove Siri, add Sleep 15 | - Shortcuts. Enable "Use keyboard navigation" 16 | - Input Sources. Disable "Show Input menu in menu bar" 17 | - Download my [Laptop script](https://github.com/lachlanjc/laptop) & execute it: 18 | 19 | ```sh 20 | curl --remote-name https://raw.githubusercontent.com/lachlanjc/laptop/main/mac 21 | sh mac 2>&1 | tee ~/laptop.log 22 | ``` 23 | 24 | - In the Internet Accounts settings, add Google account 25 | - In the Sharing settings, set the computer name 26 | - In the Security & Privacy settings: 27 | - Enable "Use your Apple Watch to unlock" 28 | - Enable Zoom’s components to allow screensharing 29 | - Download Craft, iA Writer, Things, Drafts, Dato, & Xcode from the App Store 30 | - Open 1Password, sign in, enable its default settings 31 | - In Safari settings: 32 | - Enable Extensions > 1Password 33 | - Enable Advanced > Show full website address 34 | - Enable Advanced > Show Develop menu in menu bar 35 | - In FaceTime settings (Cmd-,), disable "Calls From iPhone" 36 | - In VSCode: 37 | - Configure Prettier as the default formatter, set its formatting settings 38 | - Turn up default text size 39 | - Disable View > Appearance > Show Activity Bar 40 | - Install fonts (mine are stored in iCloud Drive) 41 | - Set up code folders (`~/src`, then grouped by GitHub org) 42 | - `mkdir src` 43 | - `cd src` 44 | - `mkdir lachlanjc` 45 | - `mkdir {OTHER_GITHUB_ORGS}` 46 | - Add `src` to Finder sidebar 47 | - Switch Finder to Column view 48 | 49 | ![Screenshot of my code folder setup in Finder](https://cloud-cmi6k5btn-lachlanjc.vercel.app/2021-03-02_6ub3tkuuykh8fvg8h1h19mj4w7vantua.png) 50 | 51 | ## Before I factory reset my Mac 52 | 53 | (Note: I’ve only used Intel Macs. Not sure if anything is different on M1 machines.) 54 | 55 | 1. Back up the whole computer. (I use the Samsung T3/T5 drives, alongside [ChronoSync](https://www.econtechnologies.com/chronosync/overview.html) since it verifies checksums after backing up, but I know people like [SuperDuper](https://www.shirt-pocket.com/SuperDuper/SuperDuperDescription.html) as well. Time Machine works in a pinch, as does dragging your user folder to the drive in Finder.) 56 | 2. Because [one is none](https://www.relay.fm/cortex/12) & to make it easier to bring the files you need to the new OS installation, copy the files you want to migrate to another drive as well. For me, I just dragged my code folder (`~/src`) to a secondary drive, since that’s what’s really valuable on my Mac. (I have some old projects that aren’t on GitHub.) 57 | -------------------------------------------------------------------------------- /sheets/2021-04-18_music_roundup_april_2021.mdx: -------------------------------------------------------------------------------- 1 | import Embed from 'react-song-embed' 2 | 3 | # Music roundup, April 2021 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /sheets/2021-05-20_music_roundup_may_2021.mdx: -------------------------------------------------------------------------------- 1 | import Embed from 'react-song-embed' 2 | 3 | # Music roundup, May 2021 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /sheets/2021-06-21_how_to_meet_twitter_friends_irl.mdx: -------------------------------------------------------------------------------- 1 | # How to meet Twitter friends IRL 2 | 3 | **Who do you meet?** People who are cool in whatever city in you’re in/going to next! Anyone whose work you respect, or came from a similar background, anyone you want to be friends with. 4 | 5 | **How do you express interest?** You just DM people that you’re around (having time pressure—when you’re in town only for a few days—often helps make something actually happen) & want to get coffee if they’re into it! Sometimes they don’t, & you shouldn’t ever push for meeting a stranger who doesn’t know you. But especially right now, people usually do! 6 | 7 | **How do you schedule the time/place?** I usually give either the time or the place, then ask them for the other. That way, the appointment doesn’t feel thrust on them, or like you’re making them do all the legwork. 8 | 9 | - For location, I mention which neighborhood I live in & the neighborhood I work in, but offer to walk/bike/transit to wherever they are, & especially if I haven’t been to their neighborhood, I try for that so I can see new places. 10 | - For time, most people don’t want optional social obligations in the morning, so I wouldn’t schedule before 11am. 11:30–1:30 is lunch, 1:30–3:30 is coffee, 3:30–5:30 is juice/boba, 5:30–7:30 is dinner, but a bakery or ice cream place would be my go-to at other times of day. 11 | - People are usually a few minutes late to social meetups, so you can be a minute or two late, but if you initiated the hangout, it’s bad form to show up late. 12 | 13 | **How do you actually hang out?** That’s the hard part to make great! 14 | 15 | - I’m indecisive/bad at multitasking in social situations, so I always look up the menu of the place we’re going beforehand to decide what to get so I’m not stressed looking at the menu. (Or if you’re getting coffee, have a standby drink; I get an iced oat milk latte in warm weather & hot dirty chai in the cold.) 16 | - When they ask how you are, never ever say “good” or something—small talk is the enemy to having a fruitful conversation. I always say a whole bunch of things about what’s going on in my life & say it kinda fast to establish a vibe that oversharing & energy are welcome in the conversation, plus it gives them jumping off points to talk/ask about. 17 | - Dig into their work, usually at the beginning. Summarize what you understand of their work, & ask about it. People can easily talk about their work & usually feel more confident in that, so it’s an easier place to start. 18 | - Having a few conversation-starters in your back pocket is good; if nothing comes to mind, I’ll ask something like “what makes you come alive” (or a variation like “what do you do that’s not for capitalism” if that’s their energy) or “what are you looking forward to,” but you have to find something natural. Silence/awkward breaks are the absolute worst. A few hours before I meet someone, I start thinking about questions I’m curious to hear from them about. Remembering those in the moment can be hard, but can make the conversation go way better, since often the most interesting topics to discuss are ones people don’t naturally bring up. 19 | - Asking for people’s life story, in however much detail they want to share, is always fun. I’ve got a version of mine that takes like 3 minutes. Mildly stalking people online beforehand to find potential points of overlap can make for fun nuggets in a conversation. 20 | - When someone asks a question that’s not a specific response, it often reveals something about them too. So ask questions back—people have a great time if they talk a lot, me included! Plus, it’s “free” material to keep things going. 21 | - If there’s not either uproarious laughter or getting into deep ideas at at least a few moments in the conversation, I usually find that means it didn’t work well enough to meet up again. That’s okay! 22 | - If you never share enough about yourself/it’s not very interesting, they won’t be intrigued enough to do it again. Make sure you dig into a topic from your life or your philosophy on something deeply enough to get somewhere non-cliché. If you’re meeting via Twitter, the role Twitter plays in both of your lives is always a great subject to dig into. 23 | - I always prompt taking a selfie at the end, but *always* get consent for how/where you might post it. 24 | 25 | Learning to carry on conversations takes years & meeting a ton of people to get good at…I’m finally feeling like I can usually carry conversations well but I’ve been working on it actively for years. If you meet some people & you clam up or the conversation goes awkward or whatever, so be it. It’s not an easy or natural skill for most people! -------------------------------------------------------------------------------- /sheets/2021-06-25_music_roundup_june_2021.mdx: -------------------------------------------------------------------------------- 1 | import Embed from 'react-song-embed' 2 | 3 | # Music roundup, June 2021 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /sheets/2021-07-20_music_roundup_july_2021.mdx: -------------------------------------------------------------------------------- 1 | import Embed from 'react-song-embed' 2 | 3 | # Music roundup, July 2021 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /sheets/2021-10-16_music_roundup_october_2021.mdx: -------------------------------------------------------------------------------- 1 | import Embed from 'react-song-embed' 2 | 3 | # Music roundup, October 2021 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /sheets/2021-11-20_music_roundup_november_2021.mdx: -------------------------------------------------------------------------------- 1 | import Song from 'react-song-embed' 2 | 3 | # Music roundup, November 2021 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /sheets/2021-12-18_music_roundup_december_2021.mdx: -------------------------------------------------------------------------------- 1 | import Song from 'react-song-embed' 2 | 3 | # Music roundup, December 2021 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /sheets/2022-01-27_migrate_reeder_articles_to_matter_with_shortcuts.mdx: -------------------------------------------------------------------------------- 1 | # Migrate Reeder articles to Matter with Shortcuts 2 | 3 | There’s been buzz recently about [Matter](https://hq.getmatter.app/), a new read-later service. I’ve wanted to give Matter a try for a bit, but have been saving all my reading links for the last year or two to [Reeder 5](https://apps.apple.com/us/app/reeder-5/id1529445840?uo=4). If I’m going to give Matter a real shot, I need articles there I want to read. 4 | 5 | Today on MacStories, Federico Viticci [shared a shortcut](https://www.macstories.net/stories/macstories-starter-pack-reverse-engineering-the-matter-api-and-my-save-to-matter-shortcut/) with which he reverse-engineered the private web API for Matter. After generating an API access token with [Matter’s Obsidian plugin](https://github.com/getmatterapp/obsidian-matter), the shortcut seamlessly allows saving any link to Matter. I realized that Reeder’s shortcuts for querying its Read Later could be connected to Federico’s Save to Matter shortcut, so quickly assembled a shortcut to do just that. 6 | 7 | ![Screenshot of the two shortcuts side-by-side on an iPad Pro](https://cloud-c4yvml1hy-lachlanjc.vercel.app/2022-01-27_img_0360.png) 8 | 9 | First, you’ll need to set up your Matter plugin in Obsidian as Federico instructs. I ran into an issue with his shortcut not correctly running with multiple items, so [download my fixed Save to Matter shortcut](https://www.icloud.com/shortcuts/b51b59661f6049f2a50ca3b831d238cd) instead, & pick the directory your Obsidian vault lives in upon installation. 10 | 11 | Second, [download the Transfer Reeder to Matter shortcut](https://www.icloud.com/shortcuts/400f5646e4fa4458a7be942068dfc048). When you run it, you’ll get a list of all the articles in your Reeder Read Later queue. Select the articles you’d like to transfer, hit Done, & they should be seamlessly added in just a few seconds. 12 | 13 | ![2 screenshots of using the shortcut & the resulting articles appearing in the Matter queue](https://cloud-37vtvt8nj-lachlanjc.vercel.app/2022-01-27_img_6372.png) 14 | 15 | *** 16 | 17 | I love when data is [easily portable](https://appstories.net/episodes/216), & Shortcuts can be a quick, reliable way of transferring your own data between systems. Enjoy the shortcuts! 18 | -------------------------------------------------------------------------------- /sheets/2022-01-30_music_roundup_january_2022.mdx: -------------------------------------------------------------------------------- 1 | import Song from 'react-song-embed' 2 | 3 | # Music roundup, January 2022 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /sheets/2022-10-17_say_something_real.mdx: -------------------------------------------------------------------------------- 1 | # Say something real 2 | 3 | I try not to speak when I don’t have a distinct point that adds something to the conversation. I don’t work on projects without intention to execute on something useful. I don’t write articles to fill space, or write papers for homework assignments with no point. I write marketing copy that communicates something distinct, newsletters with tangible updates end-to-end. We have too much noise & not enough signal; it’s a crime to merely add noise. 4 | 5 | This can make execution harder in the short term: it’s more difficult to write with a clear thesis, to speak with a clear through-line. The rigor when you’re designing a website or crafting an announcement means more iterations that take longer & feel more tiring. The quest when you’re researching to write something takes you down more rabbit holes. 6 | 7 | But long-term, the projects then *amount* to something, because every brick is constructed of something real. You don’t pick up any & discover they’re hollow. You can step back in the history & find the story, the scale, the milestones as they happened. You can make every brick open source, with nothing to hide; others can learn from your transparency, 8 | 9 | Humanity presently faces our most momentous challenges ever, challenges which ask us to become more proactive, cooperative, creative. We should hold zero space for talk that’s neither contributing in a serious way nor making the equally-critical space to relax & appreciate the world along the way. Pranks for attention, empty promises, useless points for airtime, cannot stand. 10 | 11 | Once you become averse to bullshit, you can’t go back, because anything lacking true meaning feels infested, lacking nutrition. I have barely enough time in my life to do the most critical work I see to on the issues of utmost importance; to waste the next person’s time would rob everyone of what we could do together working at the highest level. Time & attention are precious resources never to be wasted. 12 | 13 | *** 14 | 15 | I credit Zach Latta & Hack Club for sharpening my focus and inspiring me as a young person with authenticity & agency. Working on early web projects together, he constantly saw through anything with no *there*-there. At the beginning it’s brutal to realize when something lacks content, but longer-term the pursuit of true meaning amounts to a spectacular gift to give. 16 | -------------------------------------------------------------------------------- /sheets/2022-11-18_link_your_domain_to_mastodon_with_nextjs.mdx: -------------------------------------------------------------------------------- 1 | # Link Your Domain to Mastodon with Next.js 2 | 3 | I’ve been [on Mastodon](https://mastodon.social/@lachlanjc) since 2017, but recently started spending time there as my Twitter network seeks alternatives. Like any other social media profile, you’re likely already linking your profile from your personal site homepage. For Mastodon, you want to add the `rel="me"` attribute to the link, [as described here](https://docs.joinmastodon.org/user/profile/#fields), to get the verified checkmark on your website on your profile. 4 | 5 | Today [Jed Schmidt linked](https://mastodon.social/@jed/109365409255422374) to [Maarten Balliauw’s post](https://blog.maartenballiauw.be/post/2022/11/05/mastodon-own-donain-without-hosting-server.html) on linking Mastodon to your domain, so anyone could search `@anything@yourdomain.com` to find your Mastodon account. The [WebFinger](https://webfinger.net) file you want to serve on your domain at `/.well-known/webfinger` is the file you get at this URL (for me): 6 | 7 | ``` 8 | GET https://mastodon.social/.well-known/webfinger?resource=acct:lachlanjc@mastodon.social 9 | ``` 10 | 11 | While he suggests copying the file onto your site, I wanted my WebFinger served by mastodon.social’s, so I don’t have to update it if anything changes. My site uses Next.js, and their [rewrite functionality](https://nextjs.org/docs/api-reference/next.config.js/rewrites) (it’s similar for any non-Next project [on Vercel](https://vercel.com/docs/project-configuration#project-configuration/rewrites)) comes in handy here. In my `next.config.js`, [I added](https://github.com/lachlanjc/site/commit/6ce828b34808f037c2cc0bcae31695095f57d611): 12 | 13 | ```js 14 | async rewrites() { 15 | return [ 16 | { 17 | source: '/.well-known/webfinger', 18 | destination: 19 | 'http://mastodon.social/.well-known/webfinger?resource=acct:lachlanjc@mastodon.social', 20 | }, 21 | ] 22 | }, 23 | ``` 24 | 25 | This is like wildcard domain email address, so similarly to how you can email `hey@lachlanjc.com` or any other handle, you can search `anything@lachlanjc.com` on Mastodon to find [my profile](https://mastodon.social/@lachlanjc). -------------------------------------------------------------------------------- /sheets/2023-01-19_use_mdx_as_nextjs_13_server_component.mdx: -------------------------------------------------------------------------------- 1 | # Use MDX as a Next.js 13 server component 2 | 3 | Next.js 13.1.2 quietly [added support](https://github.com/vercel/next.js/pull/44651) for rendering MDX as a server component, enabling using MDX as a build step for HTML with no client-side JS overhead. It uses a faster, experimental [Rust-based MDX compiler](https://github.com/vercel/next.js/pull/41919/files). It’s not production-ready, but it works for me and wasn’t difficult to set up, so here’s how: 4 | 5 | 1. **Install dependencies.** 6 | 7 | ```bash 8 | pnpm i @mdx-js/react @next/mdx 9 | pnpm i @types/mdx --save-dev 10 | ``` 11 | 2. **Enable the experimental flag.** 12 | 13 | ```js:next.config.js 14 | /** @type {import('next').NextConfig} */ 15 | const nextConfig = { 16 | experimental: { 17 | appDir: true, 18 | mdxRs: true, 19 | }, 20 | } 21 | 22 | const withMDX = require('@next/mdx')() 23 | module.exports = withMDX(nextConfig) 24 | ``` 25 | 3. **Set up components file.** This must be in the root of your project, named `mdx-components.(js|jsx|ts|tsx)`. 26 | 27 | ```typescript:mdx-components.tsx 28 | const kebabCase = (str: string) => 29 | str.toLowerCase().split(/\W+/).filter(Boolean).join('-') 30 | 31 | export function useMDXComponents(components: { 32 | [component: string]: React.ComponentType 33 | }) { 34 | return { 35 | h2: ({ children }) =>

{children}

, 36 | ...components, 37 | } 38 | } 39 | ``` 40 | 41 | You can test if DOM is being rendered as a server component by inspecting it, then switching to the [React DevTools](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=en): if it doesn’t show up there, React isn’t running on it client-side, so it’s a server component. 42 | 43 | *** 44 | 45 | Though being able to customize component rendering only at the full site level and not per-usage is limiting, being able to use MDX as a preprocessor and ship plain HTML to the client (or ship the interactive client components without the bulk) enables using MDX in more projects where the JS payload wasn’t justified for minimal usages. 46 | 47 | For content-heavy sites like blogs & documentation sites, shipping the HTML without its representation as JSON can also obviate the Next.js “static props too large” warnings, while lessening the data & battery usage for visitors. Happy MDXing! 48 | -------------------------------------------------------------------------------- /sheets/2023-01-23_generate_nextjs_image_blur_placeholders.mdx: -------------------------------------------------------------------------------- 1 | # Custom Next.js image blur placeholders 2 | 3 | [Next.js Image Optimization](https://nextjs.org/docs/basic-features/image-optimization) is a web framework at its best: Next.js took a huge problem—image loading—that I’d wasted a ton of time making bad solutions to, and made the *ideal* implementation the default option. The first iteration had serious drawbacks from both API & UX perspectives, but the Vercel team listened, rewrote it, and the newer one is top-tier. It’s perfect vertical integration. 4 | 5 | `next/image`’s blur-up placeholder, controlled by the `blurDataURL` prop, is a nice progressive enhancement, and a massive improvement for large header backgrounds. If you’re making a low-traffic site, though, the source image for a lander will leave the cache sometimes, and visitors will be left with either nothing (if the image source is remote) or a super low-res placeholder for several seconds while the image resizes and downloads. I was noticing this on the [Dispersions website](https://dispersions.cbcampbell.com/) I made, which hosts images on a separate CDN to keep the Git repo smaller. 6 | 7 | Talking to `next/image` author [@styfle](https://styfle.dev/) recently in Indianapolis, he reminded me the `blurDataURL` generated when you statically import an image is merely the URL to a super tiny version of the image. In **production**, that’s an image encoded as a (no surprise here) base64 data URL, e.g. 8 | 9 | ``` 10 | data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoKCgoKCgsMDAsPEA4QDxYUExMUFiIYGhgaGCIzICUgICUgMy03LCksNy1RQDg4QFFeT0pPXnFlZXGPiI+7u/sBCgoKCgoKCwwMCw8QDhAPFhQTExQWIhgaGBoYIjMgJSAgJSAzLTcsKSw3LVFAODhAUV5PSk9ecWVlcY+Ij7u7+//CABEIAAUACAMBIgACEQEDEQH/xAAoAAEBAAAAAAAAAAAAAAAAAAAABwEBAQAAAAAAAAAAAAAAAAAAAwT/2gAMAwEAAhADEAAAAJ+Co//EAB0QAQABAwUAAAAAAAAAAAAAAAIDAAEEBRETQdL/2gAIAQEAAT8AytSlzLRwMHaJNXXa5fNf/8QAFxEAAwEAAAAAAAAAAAAAAAAAAAFBcf/aAAgBAgEBPwCLD//EABYRAQEBAAAAAAAAAAAAAAAAAAEAMf/aAAgBAwEBPwB1v//Z 11 | ``` 12 | 13 | In **development**, if you inspect the `StaticImport` object itself, the `blurDataURL` prop is: 14 | 15 | ``` 16 | /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ffilename.jpeg&w=8&q=70 17 | ``` 18 | 19 |
20 | 21 | Curious about the discrepancy? 22 | 23 | The source code has this comment describing it: 24 | 25 | > During `next dev`, we don't want to generate blur placeholders with webpack 26 | because it can delay starting the dev server. Instead, `next-image-loader.js` 27 | will inline a special url to lazily generate the blur placeholder at request time. 28 | 29 |
30 | 31 | That URL is resizing the image to 8px wide, then `next build` is transforming it into a data URL. Therefore, if we want a custom blur-up placeholder that’s higher-quality, or for a remote image, we can: 32 | 33 | 1. Get a thumbnail version of our image (say, 12 or 16px wide), either via the `/_next/image` URL or another resizer 34 | 2. Convert it to base64 35 | 3. Add the prop manually 36 | 37 | I wrote a quick [macOS Shortcut](https://www.icloud.com/shortcuts/3d73f6f168da45dd9403572f206fdfa5) to do this: after you install it, right-click an image in Finder, click the shortcut, then paste the prop into your Image tag. The Dispersions site now appears to load far faster, with a decent-fidelity blur-up placeholder appearing immediately in the header instead of several seconds of grey waiting. 38 | 39 | ![Screenshot of activating shortcut in macOS Finder](https://cloud-5px1wf5wr-lachlanjc.vercel.app/nextdataurl_shortcut.png) 40 | 41 | If you’re looking to generate these placeholders automatically from a remote image source, while you could [roll your own](https://github.com/vercel/platforms/blob/83c330f414fc4503e814db94175fe9860083c5fe/lib/util.ts#L13), I recommend [@joe-bell](https://joebell.co.uk/)’s [plaiceholder](https://github.com/joe-bell/plaiceholder) project instead, which makes this [super easy](https://github.com/joe-bell/plaiceholder/blob/main/examples/next/src/pages/base64/single.tsx). 42 | -------------------------------------------------------------------------------- /sheets/2023-01-25_2023_css_wishlist.mdx: -------------------------------------------------------------------------------- 1 | # 2023 CSS wishlist 2 | 3 | Dave Rupert shared his [CSS wishlist for 2023](https://daverupert.com/2023/01/css-wishlist-2023/): new specs he’d love to see implemented. While I’d be delighted to have & would use all the features he mentioned, here’s a quick list of what I’d like to use in browsers. 4 | 5 | 1. **`text-wrap: balance;`** ([spec](https://drafts.csswg.org/css-text-4/#text-wrap)). Visually unbalanced headlines are a typographic menace on the web, but solving them requires custom CSS tweaking (such as `max-width: 16ch;`), HTML character entities like ` `, or JavaScript. Having this be a single property I could apply near-globally to h1 & h2 elements would save a bunch of time in making a polished design. Until then, mastermind Shu Ding’s [React Wrap Balancer](https://react-wrap-balancer.vercel.app/) works incredibly well, and I’m excited to see support [come](https://twitter.com/shuding_/status/1617904375938322432) to [Satori](https://github.com/vercel/satori). 6 | 2. **Sibling functions** ([proposal](https://github.com/w3c/csswg-drafts/issues/4559)). One trick I rely on for visual effects and animations is adding a custom property (usually with React) for an element’s index, then using that in `calc` or `animation-delay`. As with most styling JS, over time CSS should build in the simple, common use cases directly to the language, in this case to write CSS like `animation-delay: calc(sibling-index() * 0.1s);`. If you don’t want to or can’t put custom properties in your HTML, [this post](https://crinkles.dev/writing/a-nth-child-css-trick/) explains using `:nth-child` as a workaround. (You’ll spot an index custom property in a `transform` in the “steps” section I made last week for Watershed’s [new SEC page](https://watershed.com/solutions/sec).) 7 | 3. **CSS Grid masonry layout** ([details](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout/Masonry_Layout)). The spec has been around for years, but is only available behind a flag in Firefox. The CSS solution to masonry layout will be a beautiful end to the hacks, and on websites like [Scrapbook](https://scrapbook.hackclub.com), a minor godsend compared to JavaScript-based solutions using flexbox column layouts or client-side absolute positioning. 8 | 4. **Scroll-linked animations** ([details](https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-timeline), [polyfilled demo](https://codepen.io/jh3y/pen/YzjEGMm)). Any scroll-related effects need JS right now, which sucks both as a developer & for performance. While these effects can be overused just like [AOS](https://michalsnik.github.io/aos/) used to be, when done well, add an elegant polish. 9 | 5. **Animating to `auto`.** There’s many cases where I want simple animation, like a mobile navigation menu animating up from the bottom of the screen, where being able to animate a property like `height` from `0` to `auto`. JavaScript motion libraries like Framer Motion do this automatically, but I’d like those animations to be available in native CSS. 10 | 6. Bonus: **random numbers** ([proposal](https://github.com/w3c/csswg-drafts/issues/2826#issuecomment-1204305712)). I wouldn’t use these for the majority of projects, and they’re not holding back the web platform in any big way, but random numbers would enable fluid organic shapes (think of `border-radius: random(0, 100)px random(0, 100)px;`) & complex generative art in CSS. 11 | 12 | ## Bonus readings 13 | 14 | Four CSS articles I enjoyed this week: 15 | 16 | - **[Conditional CSS](https://ishadeed.com/article/conditional-css/)** by Ahmad Shadeed describes the many, many ways to apply styles conditionally, even if CSS doesn’t have traditional `if` statements. I’ve used the delightful conditional border-radius trick he mentions on my personal homepage for awhile. 17 | - **[Day 88: CSS Motion Path](https://www.matuzo.at/blog/2023/100daysof-day88/)** by Manuel Matuzovic, who’s been doing nearly 100 days of blog posts covering new CSS features. I’d missed that [browser support](https://caniuse.com/css-motion-paths) for motion paths is looking decent! 18 | - **[The truth about CSS selector performance](https://blogs.windows.com/msedgedev/2023/01/17/the-truth-about-css-selector-performance/)** by Patrick Brosset shows how rules like “`*` is slow” don’t capture the nuance of selector performance at all, and what to watch out for instead. 19 | - **[Case Study: lynnandtonic.com 2022 refresh](https://lynnandtonic.com/thoughts/entries/case-study-2022-refresh/)** by Lynn Fisher unpacks the latest gem in Lynn’s crown of CSS accomplishments. I recently shipped my first `:has()` to production (to power a minor visual effect in the watershed.com nav), but as usual she’s come up with a remarkable use case. 20 | 21 | (Would you enjoy if I shared more links to what I’m reading about frontend? [Let me know](https://mastodon.social/@lachlanjc/109750809646355424).) 22 | -------------------------------------------------------------------------------- /sheets/2023-01-30_im_not_an_environmentalist.mdx: -------------------------------------------------------------------------------- 1 | # I’m not an environmentalist 2 | 3 | Today I saw [a chart](https://news.gallup.com/poll/348227/one-four-americans-say-environmentalists.aspx) of the percentage of Americans who self-identify as environmentalists (40%), and I realized that as much as climate is a core piece of my work & identity, I don’t use the label *environmentalist* for myself. The term brings to mind 1970s environmentalism, that in my eyes was focused on conservation and preventing development. But perhaps I’ll come around to the term. 4 | 5 | Even though my values rest firmly on the environmental side, the way I think about solving climate now brings me to solutions diametrically opposite those from that generation of environmentalism. We need to build, and fast, using the smartest versions of our brains thinking as far ahead as we can manage. We need [housing](https://edu.lachlanjc.com/2022-10-06_gw_the_housing_shortage_as_environmental_issue), carbon-free energy, carbon removal, bike lanes, EV chargers, heat pumps, desalination plants, electrical transmission, electric planes, electrofuels, we need it all. We will never get to our livable, equitable future by stopping future destruction; we must actively transform (read: electrify) our built environment in the image of our livable utopia. It’s an act of creation & transformation, not prevention. It’s *for* a vision, not against another one. 6 | 7 | Simultaneously, we must conserve nature. It’s an integral part of having an ecosystem worth keeping habitable. Not only for the brilliant innovations we’ve always derived from nature, or as a place to remind us why we’re in this fight, or to keep us healthy, but because we will never have the understanding or technology to replace what the ecosystems we inherit do. (If you don’t feel passionately about this yet, the two books that convinced me are [*Half-Earth Socialism*](https://www.half.earth/) and [*Under a White Sky*](https://bookshop.org/p/books/under-a-white-sky-the-nature-of-the-future-elizabeth-kolbert/15518975).) The traditional environmental movement was right about this, but it must be addressed in a more globally-aware way: preventing an apartment building from being built to save a specific patch of forest doesn’t make sense if those people then move out into suburban homes covering more land elsewhere. 8 | 9 | Yet I wouldn’t be surprised if over my life, I identify increasingly as an *environmentalist*. The previous paragraph is evidence I’m already on my way. 10 | 11 | When I was first coming out, I identified myself as queer and avoided identifying as gay. Part of unpacking that was realizing the part of “homosexuality” I didn’t identify with was male-ness, and coming out as non-binary clarified why. Identifying as gay lumped me in with a community I didn’t feel at home with—the one I’ve encountered in the Castro or on Fire Island, with men being creepy/exploitative toward me, a community with the ugly results of DL culture or a need to police & project a certain (masc) image still toxically embedded in its fabric. *Queer* to me felt like the new generation, where we could drop those stigmas, live out & proud, allow for a more nuanced identity that embraced expansive gender and didn’t force us all into a gay male box. And I describe myself primarily as queer. But over time, I’m increasingly fine with *gay*—even if it’s not the primary word I reach for personally, its community is not the one to shun. The queerness we have today exists as a direct result of the triumphs of the queer communities prior, as climate work today rests on the *Silent Springs* & Earth Days—and the *Inconvenient Truths*—of the prior decades. 12 | 13 | If my ~~Twitter~~ Mastodon bio says *environmentalist* at some point, I’ve made peace with the roots of the movement I’ve joined. The environmentalism of decades before me got the values right. There’s a more expansive, futuristic, optimistic vision of it possible now, one I’m actively pushing toward. There’s room enough for capitalists and socialists and climate tech bros and hardcore environmentalists in this tent. Our coalition is the only way we’ll build the future we all want. 14 | 15 | (Note: I would reply “strong environmentalist” to a poll on the topic.) 16 | 17 | *Reply to this post [**on Mastodon**](https://mastodon.social/@lachlanjc/109781324949949268).* 18 | -------------------------------------------------------------------------------- /sheets/2023-09-28_good_products_stand_alone.mdx: -------------------------------------------------------------------------------- 1 | # Good products stand alone 2 | 3 | Good products stand alone regardless of their price or complexity. Higher-tier products have more features to offer, and with more polish. While not going to all the same lengths, if you haven’t used the higher-tier offerings, simpler/cheaper ones should not feel lacking or compromised. You buy a product, not an entry in a product line to be constantly reminded of. 4 | 5 | The M1 MacBook Air or iPhone SE are the perfect examples: if you haven’t used an M2 Air or iPhone 15, neither feel like they should be *more*. Their high-end counterparts are purely supersets of their design & functionalities. They don’t feature bad designs, even if their more expensive counterparts bring nicer ones. The iPad 10 provides a hardware counterexample. Its design is obviously compromised even if you’ve never seen an iPad Pro; the process of [pairing the Apple Pencil](https://sixcolors.com/post/2022/10/2022-ipad-and-ipad-pro-review-mixed-feelings/#ipad-10th-generation) reveals a lack of consideration, production design factors you shouldn’t face every day rearing their heads. 6 | 7 | In software and visual design, this means: only use the surface area you need. Using extra surface area in a simple product to resemble more fancy/complex products dilutes/destroys the value. Simple apps don’t need tab bars just because tab bars are a common pattern; use just enough chrome to let the essence shine. With optional premium features in play, allow the entry-level product to feel sufficient even to users with no intention of upgrading. 8 | -------------------------------------------------------------------------------- /sheets/2024-04-17_10_features_nextjs_15_could_launch.mdx: -------------------------------------------------------------------------------- 1 | # 10 features Next.js 15 could launch 2 | 3 | Ahead of even [Vercel Ship](https://vercel.com/ship/ticket/dZm6qJHNWz6A), I’m already dreaming about what could come to Next.js this fall with v15 at Next.js Conf. 4 | 5 | Here’s my highest-confidence items for the Next team releasing: 6 | 7 | - **App Router is stable(r).** 2022 introduced and overpromised about App Router, 2023 brought “stability,” by which the team meant API stability, if not bug-free stability, which was widely misunderstood. Unclear how to brand improved stability as revolutionary, but this should be the main focus again this year. 8 | - **Turbopack for dev by default.** 18 months after the announcement, Turbopack supports the majority of Next.js features and is now my go-to—[99.9% of tests are passing!](https://areweturboyet.com/) Now, it should be the default: drop the `--turbo`, it’s cleaner. 9 | - **Turbopack for builds (preview).** Builds can always be faster. Pour some Rust on them. 10 | - **View Transitions.** [CSS View Transitions](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API) are already at [71% browser support](https://caniuse.com/view-transitions), and [have landed](https://webkit.org/blog/15260/release-notes-for-safari-technology-preview-192/) in Safari Technical Preview, so will likely be available in the most-used browsers by September/late this year. Mastermind Shu Ding launched a preview of [support in App Router](https://next-view-transitions.vercel.app/). Building this into Next.js itself, ideally supporting Pages Router too, would add visual flare to this release. 11 | 12 | The recently-released [Portfolio Starter Kit](https://vercel.com/templates/next.js/portfolio-starter-kit) demonstrates what’s too difficult in Next today for building content-based sites—and should be built in natively: 13 | 14 | - **Content collections.** It’s [way too much work](https://github.com/vercel/examples/blob/main/solutions/blog/app/blog/utils.ts) to grab a collection of files that are anything but JSON, whether MD(X) or images or anything else, to render a simple blog/feed. An API inspired by [Contentlayer](https://contentlayer.dev/) (RIP), [Astro Content Collections](https://docs.astro.build/en/guides/content-collections/), and [Bun’s Glob API](https://bun.sh/docs/api/glob) would make Next.js dramatically easier for many developers’ first projects with it, a personal website/blog. 15 | - **New [MDX](https://mdxjs.com/) plugin.** On the same topic, rendering MDX right now is a mess: [the official plugin](https://nextjs.org/docs/app/building-your-application/configuring/mdx) isn’t what you want to render MDX blog posts, the most common use case. (A subdirectory of posts isn’t “remote,” and shouldn’t be more complicated than embedding known-path local files.) It requires installing 4 dependencies, then the RSC/Rust implementation only supports project-wide custom components. Next’s MDX plugin should work seamlessly with content collections & RSC to render a directory’s worth of files. 16 | - **Wrap RSS support.** Frameworks like Next should make it easy for websites to be good citizens of the web, and that includes making it easy to configure metadata tags, sitemaps, RSS feeds, and more. Making an RSS feed (connected to content collections, ideally) should not require [manually assembling XML](https://github.com/vercel/examples/blob/main/solutions/blog/app/rss/route.ts); make [an API just like the sitemap one](https://nextjs.org/docs/app/api-reference/file-conventions/metadata/sitemap#generating-a-sitemap-using-code-js-ts) so RSS takes minutes to configure. 17 | - **Make [JSON-LD](https://developers.google.com/search/docs/appearance/structured-data/intro-structured-data) not feel like an antipattern.** Right now, the [official recommendation](https://github.com/vercel/examples/blob/main/solutions/blog/app/blog/%5Bslug%5D/page.tsx#L61-L66) for adding JSON-LD requires `suppressHydrationWarning` & `dangerouslySetInnerHTML`. Allowing exporting these alongside or inside metadata, with automatic TypeScript validation, would encourage making parsable pages. 18 | 19 | Two random things on the horizon I’d love to see eventually: 20 | 21 | - **Expand/stabilize [`@next/third-parties`](https://nextjs.org/docs/app/building-your-application/optimizing/third-party-libraries).** Making high-performance embeds the easiest to reach for will help raise the average standard of Next.js sites. Expand support for more non-Google content services like Vimeo, Spotify, Apple Music, etc, and more analytics services like HubSpot, Mixpanel, etc. 22 | - **next/video component.** The `next/image` component took some iterations to settle on the right API, but I can’t imagine Next.js without it anymore. Video is still super hard, and I can envision Vercel wrapping [Mux](https://www.mux.com/) with a great API (and marking up their pricing). 23 | -------------------------------------------------------------------------------- /sheets/2024-09-19_visionos_2_invests_in_core_technologies.mdx: -------------------------------------------------------------------------------- 1 | # visionOS 2 invests in core technologies 2 | 3 | Some observations after an hour with visionOS 2: 4 | 5 | - Even in the first 20 minutes, the eye/hand tracking are noticeably more accurate. I made fewer misclicks & was less frustrated. The imprecision has been a major factor in the difficulty of working on Apple Vision Pro compared to iPad. 6 | - The 3D object placement/persistence is noticeably better, too. Walking across the room from a 3D model on v1, it’d shift in place a few inches, it’s now less than half that, maybe a centimeter or two visually. Snapping models to surfaces with the little plunk noise is superior to guessing where the boundaries of objects are. I still want to snap windows onto walls, which seems like it shouldn’t be too far away. 7 | - New home/control gestures seem super useful, even if they take some time to become intuitive. I can’t believe Control Center has an even worse design/hasn’t become faster to navigate this year, especially given the work in iOS 18. 8 | - Spatialize Photos blew me away. I like that Apple is not trying to AI-generate parallax where you see behind subjects in photos; they’re making the photos appear 3D. I had assumed it visualized 2-3 layers, but it’s infinite depth: heads are round in space. Photos was already one of Vision’s strengths, but it now has to be seen to be believed. 9 | - A few scattered updates with Safari profiles, Apple Music Sing, more writing tools in Freeform, Math Notes in Notes, but no new apps—not even Weather, Find My, or Journal in compatibility, and Calendar, Voice Memos, iWork etc apps haven’t become native. What are we doing! I want to use the same Apple apps across my devices. iPad compatibility apps running in dark mode is certainly a relief, though. 10 | - There’s a lovely layer of polish across the OS. The animations for editing the Home Screen with the 3D jiggling & motion curves on swiping between pages are gorgeous. There’s subtle visual effects added into the onboarding too. 11 | 12 | visionOS 2, like watchOS 2, is the OS that should have shipped originally; it is not a new take on the OS, it simply fills in some of the original. Notably, it doesn’t change the value proposition of the device or what I do/how much I use it whatsoever. There are so few new productivity features, and no iteration on the app/windowing model it shipped with. 13 | 14 | It’s notable that Apple is investing in the core technologies that make [spatial computing work long term](/2024-03-06_long_on_spatial_computing): room scanning, 3D content placement, 3D content creation, Spatial Audio, video calls without cameras, more seamless interaction. None of these are short-term sales boosters for this overpriced device; they’re all long-term bets on what must be built up for an AR (glasses) future, the essentials for combining spatial computing (Apple Vision Pro) with ambient computing (Humane) to make the Google Glass we deserve down the road. This OS grows my confidence in the platform long-term, even if it doesn’t feel like a generous gift of features for current users. 15 | -------------------------------------------------------------------------------- /sheets/2024-10-10_promote_national_progress_like_iphones.mdx: -------------------------------------------------------------------------------- 1 | # Promote National Progress like iPhones 2 | 3 | One of the most surprising disconnects to me between dystopian stories and our current reality in the U.S. is that no level of government has a reliable mechanism by which it communicates regularly with citizens. We can’t communicate anything from local bulk trash pick-up days to new healthcare policy with any level of efficiency of reach across the nation. 4 | 5 | At every level of government but especially the federal, we are lacking the feedback loop to voters of what’s getting done. There’s no other way to explain the disconnect between Biden’s imperfect, but historically productive, agenda on climate, and his dismal approval rating by voters on the climate issue. Voters are not hearing about what their elected officials are doing. 6 | 7 | My proposed solution is simple: make a digestible, Apple-style keynote video once a year with top accomplishments, and do everything possible to make sure Americans watch it. I propose marking a National Progress Day each year, on which governments celebrate the progress we’ve made in the last year. The video, keynoted by the President, lays out the key progress across the country, handing it off to key cabinet members to outline what their department has done. Each speaks for a few minutes, with simple, clear language, focusing on how American’s lives are getting better. 8 | 9 | Step two is to make sure every American sees it. Send a postcard to every address in America with the date and information. Get it on every TV channel; the State of the Union could also be reshaped into this media event, as every TV channel is already live-streaming it. Slice it up a million ways onto every social channel and promote the videos. It’s absurd the government doesn’t have a better way of reaching citizens digitally, but this is a first step to making a single event worth tuning in for. The benefits of American unity would easily pay for the event, or as campaign marketing expenses. -------------------------------------------------------------------------------- /sheets/concerts.mdx: -------------------------------------------------------------------------------- 1 | # Concerts I’ve seen 2 | 3 | Favorite concert ever: SWEAT tour at Madison Square Garden 4 | 5 | ## 2024 6 | 7 | - Dorian Electra: Fanfare 8 | - Isaac Dunbar 9 | - Allie X: Girl with No Face 10 | - Discovery Zone, L’Rain, Crumb 11 | - Omar Apollo: God Said No 12 | - Shygirl, Charli XCX, Troye Sivan: SWEAT 13 | - Sega Bodega 14 | - Shygirl: Club Shy 15 | - A.G. Cook: Britpopmania 16 | 17 | ## 2023 18 | 19 | - Jai Paul 20 | - Yves Tumor 21 | - Yaeji 22 | - Cub Sport 23 | - Alice Longyu Gao 24 | - Shygirl 25 | - Caroline Polachek 26 | - ELIO 27 | - Daine 28 | 29 | ## 2022 30 | 31 | - Greyson Chance 32 | - Rina Sawayama x2 33 | - ELIO 34 | - Omar Apollo 35 | - Charli XCX 36 | - Beach House 37 | - Billie Eilish 38 | 39 | ## 2021 40 | 41 | - Festival: Outside Lands 42 | - Sofi Tukker 43 | - Tame Impala 44 | - mxmtoon 45 | - Caroline Polachek 46 | - Yves Tumor 47 | - Isaac Dunbar 48 | - St. Vincent 49 | 50 | ## 2019 51 | 52 | - Conan Gray x2 53 | 54 | ## 2018 55 | 56 | - Troye Sivan: Bloom 57 | -------------------------------------------------------------------------------- /sheets/hardware.mdx: -------------------------------------------------------------------------------- 1 | # Hardware setup 2 | 3 | ## Daily devices 4 | 5 | - Apple Watch Series 10, 42mm, Silver 6 | - iPhone 16 Pro, Desert Titanium, 256GB 7 | - AirPods Pro 2 8 | - AirPods Max, Pink 9 | - Daylight DC-1 10 | - [NuPhy Air60 V2](https://nuphy.com/products/air60-v2) with Wisteria keycaps & NuFolio 11 | - LAMY stylus 12 | - 2021 MacBook Pro with M1 Pro, 16”, Silver, 32GB RAM/1TB SSD 13 | - Magic Trackpad 2 14 | - [Logitech LIFT](https://www.logitech.com/en-us/products/mice/lift-vertical-ergonomic-mouse.html) vertical mouse 15 | - [Kinesis Advantage2 Keyboard](https://kinesis-ergo.com/shop/advantage2/) 16 | - [Roost Stand](https://www.therooststand.com/) 17 | - 2022 Studio Display, tilt and height-adjustable 18 | 19 | ### Less-used 20 | 21 | - 2024 Apple Vision Pro, 256GB 22 | - [WaterField Shield Case](https://www.sfbags.com/products/vision-pro-shield-case), Blue Forza 23 | - 2021 iPad mini, Purple, 256GB, 5G 24 | - Apple Pencil (with [Paperlike Pencil Grips](https://paperlike.com/products/pencil-grip-set)) 25 | - Smart Folio, English Lavender 26 | - [Compass Pro](https://www.twelvesouth.com/products/compass-pro?variant=17674732732473) stand 27 | - M1 iPad Pro, 12.9”, Silver, 1TB, 5G 28 | - Apple Pencil 29 | - Magic Keyboard for iPad 30 | - [PaperLike Charcoal Folio Case](https://paperlike.com/products/paperlike-folio-case) 31 | - [HoverBar Duo](https://www.twelvesouth.com/products/hoverbar-duo) desk stand 32 | 33 | ## Chargers 34 | 35 | For home: 36 | 37 | - [Anker 3-in-1 Cube with MagSafe](https://www.anker.com/products/y1811) for bedside 38 | - [Satechi 165W USB-C 4-Port PD GaN Charger](https://satechi.net/products/165w-usb-c-4-port-pd-gan-charger) (buy a separate C7 cable for EU/UK to have USB-C power when traveling) 39 | 40 | For on-the-go: 41 | 42 | - [Anker Nano Pro](https://www.anker.com/products/a2637?variant=42621670457494) in any bag 43 | - [Anker 735 Charger](https://www.anker.com/products/a2667?variant=41581366575254) for backpack 44 | - [Anker Power Bank (10K, Fusion, Built-In Cable)](https://www.anker.com/products/a1637?variant=43618351153302) for long days 45 | -------------------------------------------------------------------------------- /sheets/material_goods.mdx: -------------------------------------------------------------------------------- 1 | # Material goods 2 | 3 | ## Winter 2024 4 | 5 | - Allbirds M0.0NSHOT Zero, Natural Black 6 | - Rains Cargo Rain Pants Wide, Revel 7 | 8 | ## Fall 2024 9 | 10 | - Rains Bator Puffer Tote, Foam 11 | - Rains Micro Box Bag, Laser 12 | - Rains Fuse Bomber Jacket, Mineral 13 | - Loop Switch earplugs, Pink 14 | - TwelveSouth BookArc Flex, Chrome 15 | - Anker Nano Power Bank (30W, Built-In USB-C Cable), Pink 16 | - STMNT Grooming Goods Curl Cream 17 | 18 | ## Summer 2024 19 | 20 | - Cortex Brand Sidekick Notepad 21 | - NOMAD Tracking Card 22 | - Allbirds Canvas Pipers, Purple 23 | - Meller Nayah sunglasses, Pine 24 | 25 | ## Spring 2024 26 | 27 | - Daylight DC-1 28 | - LAMY stylus 29 | - NuPhy Air60 V2 with NuFolio 30 | - Apple Watch Sport Band, Sunshine 31 | 32 | ## Winter 2023 33 | 34 | - Rains Vardo Bomber, Candy 35 | - Rains Naha Pants, Candy 36 | - Apple Vision Pro 37 | - WaterField Case, Forza Blue 38 | - Material Kitchen Cook’s Set 39 | - Parachute, Soft Rib Towels, Ochre 40 | 41 | ## Fall 2023 42 | 43 | - Rains Long Jacket, Candy 44 | - Rains Wallet, Laser 45 | - Rains Alta Puffer Mittens, Candy 46 | - BTTM Triangle Tote, Citronelle 47 | - GL.iNet GL-MT3000 Wi-Fi 6 router 48 | - American Vintage scarf 49 | - Paperlike iPad cover, Charcoal 50 | 51 | ## Summer 2023 52 | 53 | - BTTM Crescent Crossbody, Yellow 54 | - OV RecNylon 1/4 Zip, Orangeade 55 | - Anker 3-in-1 charging cube 56 | 57 | ## Spring 2023 58 | 59 | - BTTM Go-Tote Mega, Lavender 60 | - Apple Solo Loop, Purple Fog 61 | - Baggu laptop sleeve 62 | - Baggu bags 63 | - Paperlike Pencil grips, Canoopsy edition 64 | - OV Cargo pants, White 65 | 66 | ## Winter 2022 67 | 68 | - Anker 65W multi charger 69 | - BTTM Fannypack, Pink 70 | - OV High Stride jacket 71 | 72 | ## Fall 2022 73 | 74 | - Allbirds Canvas Pacers, Cyan 75 | - AirPods Pro 2 76 | - BTTM City Backpack, Yellow 77 | - iPhone 14 Pro, Deep Purple 78 | -------------------------------------------------------------------------------- /vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "github": { "silent": true } 3 | } 4 | -------------------------------------------------------------------------------- /watch.json: -------------------------------------------------------------------------------- 1 | { 2 | "install": { "include": ["^package\\.json$"], "exclude": [] }, 3 | "restart": { "include": ["^\\.env$"], "exclude": [] }, 4 | "noSavedEvents": true 5 | } 6 | --------------------------------------------------------------------------------