diff --git a/.gitignore b/.gitignore
index f7a48032..b8737f59 100644
--- a/.gitignore
+++ b/.gitignore
@@ -224,3 +224,6 @@ $RECYCLE.BIN/
# End of https://www.gitignore.io/api/node,macos,windows,visualstudiocode
.env.development
+
+# Local Netlify folder
+.netlify
diff --git a/netlify.toml b/netlify.toml
index a6fd6df8..346d4c3d 100644
--- a/netlify.toml
+++ b/netlify.toml
@@ -1,6 +1,5 @@
[build]
- command = "npm run build"
- publish = "out"
+ publish = ".next"
[[plugins]]
package = "@netlify/plugin-nextjs"
diff --git a/next.config.js b/next.config.js
index 003b30cc..3c5e0e97 100644
--- a/next.config.js
+++ b/next.config.js
@@ -9,4 +9,7 @@ const withMDX = require('@next/mdx')({
module.exports = withMDX({
pageExtensions: ['js', 'jsx', 'md', 'mdx'],
+ images: {
+ domains: ['i.ytimg.com'],
+ },
});
diff --git a/package.json b/package.json
index 55713826..64762e24 100644
--- a/package.json
+++ b/package.json
@@ -14,7 +14,7 @@
"prepare": "husky install"
},
"dependencies": {
- "@chakra-ui/react": "^1.6.8",
+ "@chakra-ui/react": "^1.6.10",
"@emotion/react": "^11",
"@emotion/styled": "^11",
"@material-ui/core": "^4.12.3",
@@ -23,7 +23,7 @@
"@mdx-js/mdx": "^1.6.22",
"@mdx-js/react": "^1.6.22",
"@next/mdx": "^11.1.2",
- "date-fns": "^2.24.0",
+ "date-fns": "^2.25.0",
"fathom-client": "^3.2.0",
"feed": "^4.2.2",
"framer-motion": "^4",
@@ -32,7 +32,7 @@
"mdx-embed": "^0.0.22",
"moment": "2.29.1",
"next": "^11.1.2",
- "next-mdx-remote": "^3.0.5",
+ "next-mdx-remote": "^3.0.6",
"pluralize": "^8.0.0",
"prism-react-renderer": "^1.2.1",
"prop-types": "^15.6.2",
@@ -40,18 +40,19 @@
"react-dom": "^17.0.2",
"remark": "^14.0.1",
"remark-html": "^15.0.0",
- "sass": "^1.42.1",
+ "sass": "^1.43.2",
"sharp": "^0.29.1",
"swr": "^1.0.1",
"use-cloudinary": "^4.1.1"
},
"devDependencies": {
+ "@netlify/plugin-nextjs": "4.0.0-beta.2",
"@next/eslint-plugin-next": "^11.1.2",
"eslint": "^7.32.0",
"eslint-config-next": "^11.1.2",
"eslint-config-prettier": "^8.3.0",
"husky": ">=6",
- "lint-staged": "^11.2.0",
+ "lint-staged": "^11.2.3",
"netlify-plugin-cache-nextjs": "^1.6.1",
"prettier": "^2.4.1"
},
diff --git a/src/components/ExternalWorkFeed/ExternalWorkFeed.js b/src/components/ExternalWorkFeed/ExternalWorkFeed.js
new file mode 100644
index 00000000..706184dc
--- /dev/null
+++ b/src/components/ExternalWorkFeed/ExternalWorkFeed.js
@@ -0,0 +1,42 @@
+import Image from 'next/image';
+import Link from 'next/link';
+import { Box, Stack, Heading, Spacer, Text, useTheme } from '@chakra-ui/react';
+import { MDXRemote } from 'next-mdx-remote';
+import moment from 'moment';
+import TagsSummary from '../tagsSummary';
+
+const ExternalWorkFeed = ({ articles }) => {
+ const theme = useTheme();
+
+ return articles.map((article) => {
+ const {
+ frontmatter: { date, url, title, tags },
+ } = article;
+
+ const formattedDate = moment(new Date(date)).format('MMMM DD, YYYY');
+ return (
+
+
+
+
+ {title}
+
+
+ {formattedDate}
+
+
+
+
+
+
+ );
+ });
+};
+
+export default ExternalWorkFeed;
diff --git a/src/components/ExternalWorkFeed/index.js b/src/components/ExternalWorkFeed/index.js
new file mode 100644
index 00000000..dcc37ae1
--- /dev/null
+++ b/src/components/ExternalWorkFeed/index.js
@@ -0,0 +1 @@
+export { default as ExternalWorkFeed } from './ExternalWorkFeed';
\ No newline at end of file
diff --git a/src/components/Layouts/DefaultLayout.js b/src/components/Layouts/DefaultLayout.js
index 778c565a..f85e6d45 100644
--- a/src/components/Layouts/DefaultLayout.js
+++ b/src/components/Layouts/DefaultLayout.js
@@ -86,6 +86,9 @@ const DefaultLayout = ({ children }) => {
Blog
+
+ Work
+
About
diff --git a/src/components/index.js b/src/components/index.js
index 964e49ac..91f27d6a 100644
--- a/src/components/index.js
+++ b/src/components/index.js
@@ -1,4 +1,5 @@
export { default as Footer } from './footer';
+export { ExternalWorkFeed } from './ExternalWorkFeed';
export { Image } from './Image';
export { NewsletterSignup } from './NewsletterSignup';
export { PostFeed } from './PostFeed';
diff --git a/src/components/post.js b/src/components/post.js
index ef748ab6..e650c8d8 100644
--- a/src/components/post.js
+++ b/src/components/post.js
@@ -51,7 +51,7 @@ const Post = ({ summary, post }) => {
/>
);
- const formattedDate = moment(new Date(date)).format('DD MMMM YYYY');
+ const formattedDate = moment(new Date(date)).format('MMMM DD, YYYY');
return (
diff --git a/src/components/tag.js b/src/components/tag.js
index 99d07284..96687fb2 100644
--- a/src/components/tag.js
+++ b/src/components/tag.js
@@ -2,17 +2,23 @@ import React from 'react';
import PropTypes from 'prop-types';
import Link from 'next/link';
-import * as classes from '../styles/tag.module.scss';
+import { Link as ChakraLink, Flex, Text, useTheme } from '@chakra-ui/react';
const Tag = ({ children, url }) => {
- let tag = {children};
+ const theme = useTheme();
+
+ let tag = (
+
+ #
+ {children}
+
+ );
if (url) {
tag = (
-
- {/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
+ {tag}
-
+
);
}
return tag;
diff --git a/src/components/tagsSummary.js b/src/components/tagsSummary.js
index 97c4850a..750de527 100644
--- a/src/components/tagsSummary.js
+++ b/src/components/tagsSummary.js
@@ -11,7 +11,7 @@ const TagsSummary = ({ tags }) => {
if (!tags || tags.length <= 0) return null;
return (
-
+
{map(tags, (tag, id) => (
+
+I was cofounder and CEO of [smpl](https://smpl.io), a SaaS startup helping independent-scale coworking operators automate their business. We were featured in a 2017 Article by Axios Charlotte (then called **The Charlotte Agenda**) as one of the most powerful (lol) startups in town. Pretty cool!
diff --git a/src/data/external-references/building-the-plane-while-flying-it.mdx b/src/data/external-references/building-the-plane-while-flying-it.mdx
new file mode 100644
index 00000000..561b1577
--- /dev/null
+++ b/src/data/external-references/building-the-plane-while-flying-it.mdx
@@ -0,0 +1,11 @@
+---
+title: Gymnasium - Building the plane while flying it
+date: 10-27-2015
+tags: [edX]
+---
+
+
+
+(My part starts at 13m47s, but you should watch the whole thing!)
+
+This is a talk I gave during [Open Edx Conf 2016](https://open.edx.org) with my colleagues, comrades, and brothers in arms Andrew Miller and Jeremy Osborn, talking about how we built Gymnasium while actively experimenting with formulas for an effective MOOC.
diff --git a/src/data/external-references/charlotte-30-under-30.mdx b/src/data/external-references/charlotte-30-under-30.mdx
new file mode 100644
index 00000000..5fbac794
--- /dev/null
+++ b/src/data/external-references/charlotte-30-under-30.mdx
@@ -0,0 +1,12 @@
+---
+title: Top 30 Under 30 Future Leaders of Charlotte - Elevate Lifestyle 2016
+date: 06-07-2016
+tags: [startup]
+---
+
+
+
+Not much on the web about this these days, but if you really want to go spelunking, here's a Wayback Machine link from late 2016 that's captured the gist of it. [Wayback Machine: Top 30 Under 30 Future Leaders of Charlotte, Elevate Lifestyle 2016](http://web.archive.org/web/20161115035530/http://elevatelifestyle.com/30-under-30-2016), and an [old facebook event](https://www.facebook.com/events/1088933411127102/).
diff --git a/src/data/external-references/design-matters-zero-day.mdx b/src/data/external-references/design-matters-zero-day.mdx
new file mode 100644
index 00000000..dbd73274
--- /dev/null
+++ b/src/data/external-references/design-matters-zero-day.mdx
@@ -0,0 +1,16 @@
+---
+title: Design Matters, Hygge Zero day
+date: 04-13-2018
+tags: [design, ux]
+---
+
+
+
+These are slides from an empassioned talk I gave about design during a community event called Zero Day at [Hygge Coworking](https://wearehygge.com) in Charlotte, NC, USA.
+
+The thesis: good design is important, and you should be paying attention to good-and-bad design in the world around you. Contributing to making the world better for everyone **is** everyone's job.
+
+Slides [are available on SlideShare](https://www.slideshare.net/mbifulco1/design-matters-hygge-zero-day-93804671)
diff --git a/src/data/external-references/designing-windows-apps-from-the-ground-up.mdx b/src/data/external-references/designing-windows-apps-from-the-ground-up.mdx
new file mode 100644
index 00000000..9e1b0e98
--- /dev/null
+++ b/src/data/external-references/designing-windows-apps-from-the-ground-up.mdx
@@ -0,0 +1,11 @@
+---
+title: Designing Windows 8.1 apps, from the ground up
+date: 09-26-2013
+tags: [ux, design, windows]
+---
+
+
+
+This is a talk I gave at [Blend Conf](http://2013.blendconf.com/speakers/mike-bifulco/) in Charlotte, NC USA in 2013, when I was working for [Microsoft](https://microsoft.com) as a UX Designer. If you're interested in learning how to design apps for a version of Windows that came out in 2012, this is your day!
+
+Slides are [available on Slideshare](https://www.slideshare.net/mbifulco1/designing-windows-81-apps-from-the-ground-up)
diff --git a/src/data/external-references/driving-successful-launch-for-conversational-actions.mdx b/src/data/external-references/driving-successful-launch-for-conversational-actions.mdx
new file mode 100644
index 00000000..092e546f
--- /dev/null
+++ b/src/data/external-references/driving-successful-launch-for-conversational-actions.mdx
@@ -0,0 +1,9 @@
+---
+title: Driving a successful launch for Conversational Actions
+date: 10-01-2021
+tags: [assistant, android, YouTube]
+---
+
+
+
+From [Google I/O 2021](https://io.google/2021/): In this session, we (I) discuss marketing activities that help users discover and engage with what you’ve built on Google Assistant, as well as some updates to monetization features for Conversational Actions.
diff --git a/src/data/external-references/dynamic-shortcuts-for-assistant.mdx b/src/data/external-references/dynamic-shortcuts-for-assistant.mdx
new file mode 100644
index 00000000..8d11e8c2
--- /dev/null
+++ b/src/data/external-references/dynamic-shortcuts-for-assistant.mdx
@@ -0,0 +1,9 @@
+---
+title: Push dynamic shortcuts to Assistant
+date: 05-19-2021
+tags: [assistant, android, YouTube]
+---
+
+
+
+From my work as a Developer Advocate on the Google Assistant team, this tutorial explains the difference between _shortcuts_ and _dynamic shortcuts_ for Android, and how to use Assistant to take advantage of this functionality.
diff --git a/src/data/external-references/edx-comprehensive-theming-tutorial.mdx b/src/data/external-references/edx-comprehensive-theming-tutorial.mdx
new file mode 100644
index 00000000..d2620718
--- /dev/null
+++ b/src/data/external-references/edx-comprehensive-theming-tutorial.mdx
@@ -0,0 +1,14 @@
+---
+title: Edx Comprehensive Theming Tutorial
+date: 07-28-2016
+tags: [edX, talks]
+---
+
+
+
+A talk I gave at Stanford University in 2016 on Comprehensive Theming for [Open EdX](https://open.edx.org).
+
+Slides [are available on Slideshare](https://www.slideshare.net/mbifulco1/edx-comprehensive-tutorial)
diff --git a/src/data/external-references/how-to-create-your-first-app-action.mdx b/src/data/external-references/how-to-create-your-first-app-action.mdx
new file mode 100644
index 00000000..78c5e3ad
--- /dev/null
+++ b/src/data/external-references/how-to-create-your-first-app-action.mdx
@@ -0,0 +1,9 @@
+---
+title: How to create your first App Action
+tags: [demo, assistant, android, voice]
+date: 05-19-2021
+---
+
+
+
+A brief, 2-minute Demo from [Google I/O 2021](https://io.google/2021/): learn how to create your first App Action for an Android app, which lets you connect your app's features to Google Assistant
diff --git a/src/data/external-references/jamstack-ssgs-role-in-creator-economy.mdx b/src/data/external-references/jamstack-ssgs-role-in-creator-economy.mdx
new file mode 100644
index 00000000..e1637306
--- /dev/null
+++ b/src/data/external-references/jamstack-ssgs-role-in-creator-economy.mdx
@@ -0,0 +1,13 @@
+---
+title: JAM Stack, SSGs, and their role in empowering the creator economy
+date: 08-23-2021
+tags: [javascript, react, jamstack, ssg, eleventy]
+---
+
+
+
+Tools like Eleventy, Hugo, Gatsby, Svelte, and others have given developers a superpower for quickly developing content-driven, easily manageable web properties. It's an exciting frontier for developers who are also content creators, but for non-technical creators, our favorite toolchains can feel like a labyrinth of indecipherable choices.
+
+In this talk, we'll discuss our role in promoting the creator economy. We'll talk about the opportunity we have as developers in helping to democratize content creation for everyone, and the importance of creating a more robust, archival internet.
+
+[Check out the slides](https://docs.google.com/presentation/d/1XFJ_nQfPnX0oPOUezOEwVKLZFtptTKemtGg41RJPisQ)
diff --git a/src/data/external-references/matter-a-whole-thing-about-design.mdx b/src/data/external-references/matter-a-whole-thing-about-design.mdx
new file mode 100644
index 00000000..03f617b0
--- /dev/null
+++ b/src/data/external-references/matter-a-whole-thing-about-design.mdx
@@ -0,0 +1,14 @@
+---
+title: 'Matter - a whole thing about design'
+date: 03-07-2019
+tags: [ux, design]
+---
+
+
+
+From a talk I gave at my then-home-base [Hygge Coworking](https://wearehygge.com/) at a community event. The thesis: we're all designers, and you should give a damn about the things you put into the world.
+
+Slides are [available on slideshare](https://www.slideshare.net/mbifulco1/matter-a-whole-thing-about-design).
diff --git a/src/data/external-references/new-transaction-features-for-smart-displays.mdx b/src/data/external-references/new-transaction-features-for-smart-displays.mdx
new file mode 100644
index 00000000..c5a83bff
--- /dev/null
+++ b/src/data/external-references/new-transaction-features-for-smart-displays.mdx
@@ -0,0 +1,9 @@
+---
+title: New Transaction Features for Smart Displays
+date: 05-19-2021
+tags: [assistant, demo, voice]
+---
+
+
+
+A quick demo from [Google I/O 2021](https://io.google/2021/): we take a look at some user experience (UX) changes for card verification code (CVC) confirmation and new payment method entry for payments on smart screens.
diff --git a/src/data/external-references/open-edx-slack-intercom.mdx b/src/data/external-references/open-edx-slack-intercom.mdx
new file mode 100644
index 00000000..72c5fbe2
--- /dev/null
+++ b/src/data/external-references/open-edx-slack-intercom.mdx
@@ -0,0 +1,11 @@
+---
+title: Open EdX, Slack, and Intercom
+date: 07-17-2016
+tags: [edx]
+---
+
+
+
+(My talk starts at 5 minutes and 43 seconds into the video)
+
+From [Open EdX Conf 2016](https://open.edx.org), this is a lightning talk I gave on our strategy for student support at [Gymnasium](https://thegymnasium.com), while I served as Technical Director there.
diff --git a/src/data/external-references/publishing-your-first-github-pages-website.mdx b/src/data/external-references/publishing-your-first-github-pages-website.mdx
new file mode 100644
index 00000000..26e24b6a
--- /dev/null
+++ b/src/data/external-references/publishing-your-first-github-pages-website.mdx
@@ -0,0 +1,13 @@
+---
+title: Publishing Your First GitHub Pages Website - Gymnasium Take 5
+date: 03-01-2019
+tags: [html, github]
+---
+
+
+
+From my days working as Technical Director of [thegymnasium.com](https://thegymnasium.com): a 5-minute tutorial (lovingly referred to as a **Take 5**) showing how you can use GitHub pages to host your very own website.
+
+The full tutorial (with additional reference material) is available **for free** on [The Gymnasium](https://thegymnasium.com/courses/take5/publishing-your-first-github-pages-website)
+
+Gymnasium is a treasure - if you like this, get yourself a free account at [thegymnasium.com/register](https://thegymnasium.com/register), and start learning at your own pace today!
diff --git a/src/data/external-references/smpl-acquired-by-proximity.mdx b/src/data/external-references/smpl-acquired-by-proximity.mdx
new file mode 100644
index 00000000..78393c84
--- /dev/null
+++ b/src/data/external-references/smpl-acquired-by-proximity.mdx
@@ -0,0 +1,28 @@
+---
+title: Proximity Acquires SMPL Software
+date: 04-08-2020
+tags: [startup, react, firebase, stripe, entrepreneurship, founder, smpl]
+---
+
+
+
+In early 2020, [smpl](https://smpl.io) was acquired by [Proximity](https://proximity.space). I was cofounder and CEO of smpl - we proudly helped independent coworking spaces manage their communities with automated billing, invoicing, and community management. It was a wild ride, and I couldn't have done it without my two amazing cofounders.
+
+We were acquired by our biggest competitor, Proximity, who are continuing the dream by creating a network of amazing, independently owned coworking spaces around the world.
+
+[Read the press release about the acquisition](https://www.proximity.space/news/proximity-acquires-smpl-software/) to find out more.
+
+There is another (paywalled) article on [Business Insider](https://www.businessinsider.com/inside-a-coworking-software-deal-proximity-buys-smpl-2020-4) about the acquisition, too!
+
+[More about smpl](https://www.crunchbase.com/organization/smpl-llc) on Crunchbase.
+
+Tools we used to build smpl:
+
+- [Firebase](https://firebase.google.com/)
+- [Stripe](https://www.stripe.com)
+- [React](https://reactjs.org)
+- [Intercom](https://intercom.com)
+- [LogRocket](https://logrocket.com)
diff --git a/src/data/external-references/the-perils-of-obedience.mdx b/src/data/external-references/the-perils-of-obedience.mdx
new file mode 100644
index 00000000..bd4a4008
--- /dev/null
+++ b/src/data/external-references/the-perils-of-obedience.mdx
@@ -0,0 +1,18 @@
+---
+title: 'Lightning Talk: The Perils of Obedience'
+date: 08-10-2018
+tags: [philosophy, vote]
+---
+
+
+
+These are slides from a lightning talk I gave at a local conference in 2018.
+
+I explain the Milgram Experiment, how absolutely terrifying it is, and how understanding Cognitive Psychology and Social Sciences can help us make the world a better place.
+
+I also implore the audience to go register to vote. [turbovote.org](https://turbovote.org). Go do it!
+
+Slides [available on Slideshare](https://www.slideshare.net/mbifulco1/the-perils-of-obedience) if you're interested in perusing.
diff --git a/src/data/external-references/voice-talks-ux-for-voice.mdx b/src/data/external-references/voice-talks-ux-for-voice.mdx
new file mode 100644
index 00000000..58472b1d
--- /dev/null
+++ b/src/data/external-references/voice-talks-ux-for-voice.mdx
@@ -0,0 +1,9 @@
+---
+title: 'VOICE Talks August 2021: Trust the researchers for voice-first UX'
+date: 08-27-2021
+tags: [assistant, voice, devrel, ux]
+---
+
+
+
+As a part of my work as a DevRel lead for Google Assistant, I did a guest segment on VOICE Talks in August of 2021, talking about why UX research for voice-first experiences is so crucial.
diff --git a/src/data/external-references/what-the-heck-is-a-fullstack-developer.mdx b/src/data/external-references/what-the-heck-is-a-fullstack-developer.mdx
new file mode 100644
index 00000000..1c355e4c
--- /dev/null
+++ b/src/data/external-references/what-the-heck-is-a-fullstack-developer.mdx
@@ -0,0 +1,23 @@
+---
+title: What the Heck is a Fullstack Developer? Aquent UK
+date: 07-14-2020
+tags: [dev, javascript]
+---
+
+
+
+An interview with my former employer (and parent company of [Gymnasium](https://www.thegymnasium.com/)), [Aquent](https://aquent.com) about my thoughts on what it means to be a Fullstack Developer.
+
+Description from the article:
+
+
+
+Full article is available to read on [Aquent UK's blog](https://aquent.co.uk/blog/what-the-heck-is-a-full-stack-developer).
diff --git a/src/data/external-references/your-app-is-ugly.mdx b/src/data/external-references/your-app-is-ugly.mdx
new file mode 100644
index 00000000..3236d9d6
--- /dev/null
+++ b/src/data/external-references/your-app-is-ugly.mdx
@@ -0,0 +1,13 @@
+---
+title: Your App is Ugly
+date: 05-13-2016
+tags: [design, ux]
+---
+
+
+
+A rehash of a talk on the basics of design and aesthetics, covering color theory, swiss design, and some of the historical roots of modern design.
+
+Authored with Andrew Miller, Jeremy Osborn, and Leah Cunningham
+
+Slides [are available on SlideShare](https://www.slideshare.net/mbifulco1/your-app-is-ugly)
diff --git a/src/data/posts/working-in-public.mdx b/src/data/posts/working-in-public.mdx
index 3036d8f5..008965b6 100644
--- a/src/data/posts/working-in-public.mdx
+++ b/src/data/posts/working-in-public.mdx
@@ -2,8 +2,8 @@
title: 'Book notes: Working In Public'
type: post
date: 11-30-2020
-excerpt: "My review of Working in Public: The Making and Maintenance of Open Source Software by Nadia Eghbal"
-coverImagePublicId: "posts/working-in-public/working-in-public"
+excerpt: 'My review of Working in Public: The Making and Maintenance of Open Source Software by Nadia Eghbal'
+coverImagePublicId: 'posts/working-in-public/working-in-public'
published: true
path: working-in-public
tags: [developer, tools, productivity, book]
@@ -21,23 +21,27 @@ Below is a pretty raw copy/paste of my notes on _Working in Public_, which I tak
-
- Developers don't contribute to open source for lack of technical ability, but rather due to fear of committing a faux pas
+ Developers don't contribute to open source for lack of technical ability,
+ but rather due to fear of committing a faux pas
- There's a huge hurdle to get over here, particularly for new contributors. Even
sending a very small PR or opening an issue has a mental tax associated with it
that can prevent progress, community from being built
-
+
The process of getting a change approved depends, among other things, on the
**complexity** of the change (and the complexity of the project), as well as
one's **reputation** among those with the ability to approve the change.
-
+
- Reputation is a tricky currency, particularly when some folks build clout by
- being funny / sarcastic to gain viral notoriety. This doesn't necessarily equate
- to value to the community or trustworthiness, but often can be parlayed into exactly
- that.
+ being funny / sarcastic to gain viral notoriety. This doesn't necessarily equate
+ to value to the community or trustworthiness, but often can be parlayed into exactly
+ that.
+
-
+
If a project's contributor base is growing rapidly, and there is enough work
to hand off, maintainers may start to distribute work more widely. In a
@@ -51,10 +55,9 @@ Below is a pretty raw copy/paste of my notes on _Working in Public_, which I tak
is critical to success.
-
-
- Healthy open source (re: node.js contribution policy)
-
-
+
+ Healthy open source (re: node.js contribution policy)
+
- Design a contribution policy and code of conduct which entices contributors **and** retains them. Based on my experience, this is the magic in getting something over the hump from being fledgling to more broadly successful.
## Chapter 3: Roles, incentives, and relationships
@@ -64,15 +67,13 @@ Below is a pretty raw copy/paste of my notes on _Working in Public_, which I tak
- A theory of the commons
- In the last half of the 1900s, economist Elinor Ostrom came up with an 8-part theory to what makes up a successful "Commons", or resource that is owned, used, and managed by a community. These rules outline the basic tenets of what makes a successful commons, and apply directly to successful OSS in many ways.
- Making sure members are biased toward **working together** is a key to success. This may mean distributing knowledge and capability so that everyone has value and meaning, and no one person can hold up the process. Obviously encouraging collaboration is a valuable outcome here, too - so empowering folks to use each other as a resource is extremely helpful.
--
-
+-
Intrinsic motivation makes it easier for people to self-organize to achieve
the same outcome.
- The newcomer effect
- -
-
+ -
"Because newcomers have not yet developed commitment to the group and have
not yet learned how the group operates, it is rational for established
group members to distrust them"
@@ -96,22 +97,20 @@ Below is a pretty raw copy/paste of my notes on _Working in Public_, which I tak
## Chapter 4: The Work Required by Software
--
- Software, once written is never really finished. Chasing the feeling of a "finished" app/program/site is something that took me FOREVER to get over. I never felt like I had done anything, because nothing ever got to the finish line. It turns out that the finish line doesn't exist, and a decade+ of building "incomplete" things was the catalyst that I needed to figure that out. As such, greenfield projects are "coveted" because they're a blank canvas, enabling us to chase that feeling of a fresh _starting line_ to point us to a goal that is always just past the horizon. It's a feeling of hope - and thankfully a beneficial one, as rebuilding software from scratch is often inherently beneficial. Neat.
--
- Free as in speech, not as in beer vs free as in puppy is a brilliant analogy
--
-
+- Software, once written is never really finished. Chasing the feeling of a "finished" app/program/site is something that took me FOREVER to get over. I never felt like I had done anything, because nothing ever got to the finish line. It turns out that the finish line doesn't exist, and a decade+ of building "incomplete" things was the catalyst that I needed to figure that out. As such, greenfield projects are "coveted" because they're a blank canvas, enabling us to chase that feeling of a fresh _starting line_ to point us to a goal that is always just past the horizon. It's a feeling of hope - and thankfully a beneficial one, as rebuilding software from scratch is often inherently beneficial. Neat.
+- Free as in speech, not as in beer vs
+ free as in puppy
+ is a brilliant analogy
+-
By focusing too much on iterating upon their incumbent product, companies
risk missing major opportunities for so-called "disruptive innovation,"
- which eventually replaces existing products. - Clayton Christensen in Book: The Innovator's Dilemma
+ which eventually replaces existing products. - Clayton Christensen in Book:
+ The Innovator's Dilemma
-
- The anecdote about Sophie Alpert being offered $600 to **open a PR on someone's project** because it would draw positive attention is mind-blowing.
- "Is this what it feels like to be an influencer?" - Sophie
--
-
+-
We can think of a creator's reputation as a "battery," or store of value,
for attention. More followers mean more attention in the bank, but when
people follow a creator they do so because they expect to receive more
@@ -122,13 +121,10 @@ Below is a pretty raw copy/paste of my notes on _Working in Public_, which I tak
## Chapter 5: Managing the Costs of Production
--
- Absolutely mind-blowing that at one point, cryptographic code was treated the same as munitions by the US government. Early developers of OpenSSL had to become licensed arms dealers to be able to write and "export" (i.e., distribute) their code.
+- Absolutely mind-blowing that at one point, cryptographic code was treated the same as munitions by the US government. Early developers of OpenSSL had to become licensed arms dealers to be able to write and "export" (i.e., distribute) their code.
- This is an implicit declaration that certain types of technical solutions can be weaponized, and perhaps just as importantly that sometime in the past, some of our legislators had a reasonable understanding of this. In the US, at least, it's apparent that this is no longer the case. Don't believe me? Pull up a congressional hearing with Tim Cook, Zuckerberg, etc
- Relatedly, it is asymptotically difficult for lawmakers to keep up with the changing pace of technology. There's a decent argument to be made for a citizen-run, or commons-run governing body for technical regulation, rather that trusting some crusty old white men from flyover states whose pre-government professional lives weren't the least bit technical.
--
-
-
+-
When software is in static state, **what if there is no free-rider
problem?** When code is non-rivalrous, it only has first-copy costs, which
the creator is intrinsically motivated to provide -- so the problem doesn't
@@ -138,8 +134,7 @@ Below is a pretty raw copy/paste of my notes on _Working in Public_, which I tak
- This hints at one of the most fascinating things about content creators online. Virtually all of the really, truly successful creators are driven by an enjoyment of creating and sharing -- and might not typically start creating as a business. Often times it takes hundreds of youtube videos, or dozens of podcast episodes, blog posts, etc to get any kind of sizeable following. It's those with the fortitude and perseverance to continue without an audience that find success. Perhaps the audience is their reward, or perhaps the audience is a natural byproduct of the law of large numbers: create enough of something, and put it out into the world, and **some size** of audience will find you.
--
-
+-
At a high level, it's also possible that throwing away projects and starting
fresh is the most cos-effective approach: rebuilding the whole system, piece
by piece.
diff --git a/src/lib/blog.js b/src/lib/blog.js
index d9a1bb22..00841361 100644
--- a/src/lib/blog.js
+++ b/src/lib/blog.js
@@ -35,8 +35,11 @@ export function getAllPosts() {
/// filter out drafts for production
if (process.env.NODE_ENV === 'production') {
- return posts.filter((post) => post.frontmatter.published === true);
+ return posts.filter((post) => post.frontmatter?.published !== false);
}
return posts;
-}
+}
+
+export const getAllPostsByTag = (tag) =>
+ getAllPosts().filter((post) => post?.frontmatter?.tags?.includes(tag)) || [];
diff --git a/src/lib/contentTypeLoader.js b/src/lib/contentTypeLoader.js
new file mode 100644
index 00000000..bf3a1069
--- /dev/null
+++ b/src/lib/contentTypeLoader.js
@@ -0,0 +1,43 @@
+// Install gray-matter and date-fns
+import matter from 'gray-matter';
+import { parse, compareDesc } from 'date-fns';
+import fs from 'fs';
+import { join } from 'path';
+
+export function getContentBySlug(slug, directory) {
+ const realSlug = slug.replace(/\.mdx$/, '');
+ const fullPath = join(directory, `${realSlug}.mdx`);
+ const fileContents = fs.readFileSync(fullPath, 'utf8');
+ const { data, content } = matter(fileContents);
+
+ // store date in frontmatter as milliseconds since epoch
+ const date = parse(data.date, 'MM-dd-yyyy', new Date()).getTime();
+
+ return {
+ slug: realSlug,
+ frontmatter: {
+ ...data,
+ date,
+ },
+ content,
+ };
+}
+
+export function getAllContentFromDirectory(directory) {
+ const slugs = fs.readdirSync(directory);
+ const articles = slugs.map((slug) => getContentBySlug(slug, directory));
+
+ // sort posts by date, newest first
+ articles.sort((a, b) =>
+ compareDesc(a?.frontmatter.date, b?.frontmatter.date)
+ );
+
+ /// filter out drafts for production
+ if (process.env.NODE_ENV === 'production') {
+ return articles.filter(
+ (article) => articles.frontmatter?.published !== false
+ );
+ }
+
+ return articles;
+}
diff --git a/src/lib/external-references.js b/src/lib/external-references.js
new file mode 100644
index 00000000..cb8e1b8b
--- /dev/null
+++ b/src/lib/external-references.js
@@ -0,0 +1,32 @@
+import { join } from 'path';
+
+import {
+ getAllContentFromDirectory,
+ getContentBySlug,
+} from './contentTypeLoader';
+
+// directory reference to `src/content/external-references`
+const externalReferencesDirectory = join(
+ process.cwd(),
+ 'src',
+ 'data',
+ 'external-references'
+);
+
+export const getAllExternalReferencesBySlug = (slug) =>
+ getContentBySlug(slug, externalReferencesDirectory);
+
+export const getAllExternalReferences = () => {
+ return getAllContentFromDirectory(externalReferencesDirectory).map(
+ (article) => {
+ return {
+ ...article,
+ };
+ }
+ );
+};
+
+export const getAllExternalReferencesByTag = (tag) =>
+ getAllExternalReferences().filter(
+ (article) => article?.frontmatter?.tags?.includes(tag) || []
+ );
diff --git a/src/lib/tags.js b/src/lib/tags.js
new file mode 100644
index 00000000..0e3c61c2
--- /dev/null
+++ b/src/lib/tags.js
@@ -0,0 +1,24 @@
+import { getAllPosts } from './blog';
+import { getAllExternalReferences } from './external-references';
+
+export const getAllTags = async () => {
+ const blogPostTags = new Set();
+ const articleTags = new Set();
+
+ const allPosts = await getAllPosts();
+ const allExternalReferences = await getAllExternalReferences();
+
+ allPosts.forEach((post) => {
+ post?.frontmatter?.tags?.forEach((tag) => blogPostTags.add(tag));
+ });
+
+ allExternalReferences.forEach((externalReference) => {
+ externalReference?.frontmatter?.tags.forEach((tag) => articleTags.add(tag));
+ });
+
+ return {
+ allTags: new Set([...blogPostTags], [...articleTags]),
+ postTags: blogPostTags,
+ externalReferenceTags: articleTags,
+ };
+};
diff --git a/src/pages/_app.js b/src/pages/_app.js
index 7e7ea400..981488dc 100644
--- a/src/pages/_app.js
+++ b/src/pages/_app.js
@@ -28,7 +28,7 @@ function MyApp({ Component, pageProps }) {
return () => {
router.events.off('routeChangeComplete', onRouteChangeComplete);
};
- }, []);
+ });
return (
diff --git a/src/pages/tags/[tag].js b/src/pages/tags/[tag].js
new file mode 100644
index 00000000..a33199fc
--- /dev/null
+++ b/src/pages/tags/[tag].js
@@ -0,0 +1,62 @@
+import React from 'react';
+
+import { getAllPostsByTag } from '../../lib/blog';
+import { DefaultLayout } from '../../components/Layouts';
+import { SEO } from '../../components';
+import { Heading, Text } from '@chakra-ui/layout';
+import { getAllTags } from '../../lib/tags';
+import { Box } from '@chakra-ui/react';
+import { getAllExternalReferencesByTag } from '../../lib/external-references';
+
+export async function getStaticProps({ params }) {
+ const { tag } = params;
+
+ return {
+ props: {
+ tag,
+ posts: getAllPostsByTag(tag),
+ articles: getAllExternalReferencesByTag(tag),
+ },
+ };
+}
+
+export async function getStaticPaths() {
+ const tags = await getAllTags();
+
+ console.log(tags);
+
+ const { allTags } = tags;
+
+ const paths = [];
+
+ for (let tag of allTags) {
+ paths.push({
+ params: {
+ tag,
+ },
+ });
+ }
+
+ return {
+ paths,
+ fallback: false,
+ };
+}
+
+const TagPage = ({ tag, posts, articles }) => {
+ return (
+
+
+
+ #
+ {tag}
+
+
+ {posts.length} posts
+ {articles.length} articles
+
+
+ );
+};
+
+export default TagPage;
diff --git a/src/pages/work.js b/src/pages/work.js
new file mode 100644
index 00000000..56db5fbc
--- /dev/null
+++ b/src/pages/work.js
@@ -0,0 +1,61 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+import { Box, Heading, Stack, Text, useTheme } from '@chakra-ui/react';
+import { serialize } from 'next-mdx-remote/serialize';
+
+import { DefaultLayout } from '../components/Layouts';
+import { ExternalWorkFeed, SEO } from '../components';
+
+import { getAllExternalReferences } from '../lib/external-references';
+
+export async function getStaticProps() {
+ const articles = await Promise.all(
+ getAllExternalReferences().map(async (article) => {
+ const mdxSource = await serialize(article.content);
+ return {
+ ...article,
+ source: mdxSource,
+ };
+ })
+ );
+
+ return {
+ props: {
+ articles,
+ },
+ };
+}
+
+const WorkPage = ({ articles }) => {
+ const theme = useTheme();
+ return (
+
+
+
+ Some samples of my work online
+
+
+ This page contains articles, videos, and other references to my work
+ over the years. {"I'm"} extremely lucky to be able to say that the
+ nature of some of my work is that it is recorded for the public to
+ see, and that {"I've"} made news headlines from time to time (for
+ good reasons!) — {"there's"} quite a bit of my work that{' '}
+ {"isn't"} represented here, too. {"I'm"} always happy to talk shop.{' '}
+ drop me a line if{' '}
+ {"you'd"} like to know more!
+
+
+
+
+
+
+
+ );
+};
+
+WorkPage.propTypes = {
+ articles: PropTypes.arrayOf(PropTypes.shape({})),
+};
+
+export default WorkPage;
diff --git a/src/styles/post.module.scss b/src/styles/post.module.scss
index 69bf7485..1bde53c2 100644
--- a/src/styles/post.module.scss
+++ b/src/styles/post.module.scss
@@ -23,12 +23,12 @@
text-decoration: underline;
}
-.post a:hover {
+.post header a {
text-decoration: none;
}
-.post header a {
- text-decoration: none;
+.post header a:hover {
+ text-decoration: underline;
}
.post p {
@@ -97,5 +97,5 @@ body.dark-mode .post h6 a {
}
.post p {
- max-width: 50remnata;
+ max-width: 50rem;
}
diff --git a/src/styles/tag.module.scss b/src/styles/tag.module.scss
deleted file mode 100644
index 4ca48d74..00000000
--- a/src/styles/tag.module.scss
+++ /dev/null
@@ -1,15 +0,0 @@
-.container {
- padding: 0.25rem 0.5rem;
- display: inline-block;
- border-radius: 0.1rem;
- background-image: linear-gradient(to top, #e26015, #fe5186);
- margin: 0.5rem 0.5rem 0.5rem 0;
- color: white;
- font-weight: bold;
-}
-
-.link {
- &:hover span {
- text-decoration: underline;
- }
-}
diff --git a/src/utils/MDXProviderWrapper.js b/src/utils/MDXProviderWrapper.js
index b054cd32..75131faf 100644
--- a/src/utils/MDXProviderWrapper.js
+++ b/src/utils/MDXProviderWrapper.js
@@ -4,7 +4,15 @@ import React from 'react';
import { MDXProvider } from '@mdx-js/react';
import { MDXEmbedProvider } from 'mdx-embed';
-import { Button, Code, Box, Heading, Text, useTheme } from '@chakra-ui/react';
+import {
+ Button,
+ Code,
+ Box,
+ Heading,
+ Link,
+ Text,
+ useTheme,
+} from '@chakra-ui/react';
import PrismHighlight, { defaultProps } from 'prism-react-renderer';
import prismTheme from 'prism-react-renderer/themes/nightOwl';
@@ -22,6 +30,12 @@ const H6 = (props) => ;
const P = (props) => (
);
+
+const CustomLink = (props) => {
+ const theme = useTheme();
+ return ;
+};
+
const Aside = (props) => {
const theme = useTheme();
@@ -62,7 +76,7 @@ const InlineCode = (props) => {
{...props}
/>
);
-}
+};
const Pre = (props) => {
const classNames = props.children.props.className || '';
@@ -85,8 +99,10 @@ const Pre = (props) => {
// TODO: why is this needed though?
if (i === tokens.length - 1) return null;
return (
+ // eslint-disable-next-line react/jsx-key