Thanks to visit codestin.com
Credit goes to levels.io

, not inside the chart sections, so they need naming separately. */ .blogstats, .evchart, .folchart, .sentchart, .subchart, .tagwall, .vcfund, .fol-tip, .sc-tip, .svot-tip, .vcf-tip { font-variant-numeric: tabular-nums; } * { box-sizing: border-box; } html { background: var(--bg); } body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; background: var(--bg); color: var(--fg); line-height: 1.65; margin: 0; } .theme-toggle { position: absolute; top: 1rem; right: 1rem; z-index: 10; background: none; border: none; cursor: pointer; font-size: 1.2rem; line-height: 1; padding: .35rem; color: var(--muted); opacity: .7; } .theme-toggle:hover { opacity: 1; } .subscribe-top { position: absolute; top: 1rem; right: 3.2rem; z-index: 10; background: var(--accent); color: #fff; font-size: .8rem; font-weight: 600; padding: .35em .85em; border-radius: 6px; border: 1px solid var(--accent); } /* Same hover treatment as .catch-emails-button: outlined ghost style. */ .subscribe-top:hover { background: none; color: var(--accent); text-decoration: none; } .wrap { max-width: 42rem; margin: 0 auto; padding: 3rem 1.25rem 5rem; } a { color: var(--accent); text-decoration: none; } a:hover { text-decoration: underline; } header.site { margin-bottom: 2.5rem; } header.site h1 { font-size: 1.4rem; margin: 0; } header.site h1 a { color: var(--fg); } header.site p { color: var(--muted); margin: .35rem 0 0; } .year { color: var(--heading); font-weight: 700; margin: 2.5rem 0 .75rem; font-size: 1.4em; } /* Date separator inside a post list — centred label with a hairline rule running out to each side. Used for both granularities (month when the list is dense, year when it isn't), so the two never look different. Distinct from .year, which is a section heading (Pinned, Related posts). */ .datesep { display: flex; align-items: center; gap: .75em; color: var(--muted); font-weight: 700; font-size: 1em; margin: 1.7rem 0 .6rem; } .datesep::before, .datesep::after { content: ""; flex: 1 1 0; height: 1px; background: var(--border); } .datesep a { color: inherit; text-decoration: none; } .datesep a:hover { color: var(--accent); text-decoration: underline; } .meta a { color: inherit; text-decoration: none; } .meta a:hover { color: var(--accent); text-decoration: underline; } .posts + .datesep { margin-top: 1.4rem; } ul.posts { list-style: none; padding: 0; margin: 0; } ul.posts li { display: flex; gap: 1rem; padding: .35rem 0; align-items: baseline; } ul.posts li time { color: var(--muted); font-variant-numeric: tabular-nums; font-size: .85rem; white-space: nowrap; flex: none; width: 4.6rem; } ul.posts li a { font-weight: 700; } ul.posts li .from-x { color: var(--muted); font-size: .85em; flex: none; margin-left: auto; } .draft-badge { font-size: .68em; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: #b45309; background: #fef3c7; border-radius: 4px; padding: .1em .4em; flex: none; } html[data-theme="dark"] .draft-badge { color: #fcd34d; background: #3a2e0c; } .draft-notice { font-size: .9em; font-weight: 600; color: #b45309; background: #fef3c7; border-radius: 6px; padding: .4em .7em; margin: 0 0 1rem; display: inline-block; } html[data-theme="dark"] .draft-notice { color: #fcd34d; background: #3a2e0c; } /* "Covers" toggle: clickable thumbnail (or empty placeholder) after the date. */ ul.posts li .cover-link { flex: none; line-height: 0; } ul.posts li .cover-thumb { display: block; width: 72px; height: 48px; object-fit: cover; border-radius: 4px; background: var(--border); transition: opacity .15s; } ul.posts.covers li { align-items: center; } /* Cap listing titles at 2 lines so a long title never wraps to 3+ and leaves the date/thumbnail floating in the middle. Full title shows on hover + the post page. */ ul.posts.covers li a:not(.cover-link) { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden; min-width: 0; } /* Thumb + title behave as one link: hovering either underlines the title and dims the thumb; clicking dims it further. */ ul.posts li:has(.cover-link:hover) a:not(.cover-link) { text-decoration: underline; } ul.posts li .cover-link:hover .cover-thumb, ul.posts li:has(a:not(.cover-link):hover) .cover-thumb { opacity: .75; } ul.posts li .cover-link:active .cover-thumb, ul.posts li:has(a:not(.cover-link):active) .cover-thumb { opacity: .5; } .sort-sep { margin-left: 1rem; } .notice { text-align: center; font-size: 1.3rem; font-weight: bold; line-height: 1.5; margin: 1rem 0 2.5rem; color: var(--heading); } .notice a { color: var(--accent); } article h1.title { font-size: 2rem; line-height: 1.2; margin: 0 0 .25rem; } article .meta { color: var(--muted); font-size: .9rem; margin-bottom: .75rem; } article .meta .meta-link { color: inherit; text-decoration: none; } article .meta .meta-link:hover { text-decoration: underline; } .post-tags { margin-bottom: 2rem; display: flex; flex-wrap: wrap; gap: .5rem; } .post-tags a { font-size: .8rem; font-weight: 600; color: var(--accent); border: 1px solid var(--border); border-radius: 999px; padding: .15rem .7rem; } .post-tags a:hover { text-decoration: none; background: var(--accent); color: #fff; border-color: var(--accent); } .tags-index { margin-top: 1.5rem; } .tags-index .tag-count { color: var(--muted); font-weight: 400; } .tags-index a:hover .tag-count { color: #fff; } article img, article video { max-width: 100%; height: auto; border-radius: 4px; display: block; margin: 1.75rem auto; } /* Ghost image figures: kill the browser's default 40px figure margin + center */ article figure.kg-image-card, article figure.kg-card-hascaption { margin: 1.75rem 0; text-align: center; } article figure.kg-image-card img, article figure.kg-card-hascaption img { margin: .5rem auto; } article figcaption { color: var(--muted); font-size: .85rem; margin-top: .4rem; text-align: center; } article img { background-color: var(--code-bg); background-image: linear-gradient(90deg, transparent, rgba(128,128,128,.12), transparent); background-size: 200% 100%; animation: img-shimmer 1.4s ease-in-out infinite; } article video { width: 100%; background: #000; } @keyframes img-shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } } figure.kg-embed-card { margin: 2rem 0; } figure.kg-embed-card iframe { max-width: 100%; } figure.kg-embed-card .twitter-tweet, figure.kg-embed-card .twitter-tweet-rendered { width: 100% !important; max-width: 100% !important; margin-left: auto; margin-right: auto; } figure.kg-embed-card .twitter-tweet-rendered iframe { width: 100% !important; max-width: 100% !important; } /* YouTube/Vimeo oembed iframes ship with fixed width/height attrs. Many old posts have them BARE (not wrapped in figure.kg-embed-card), so the generic `article iframe{height:auto}` rule below collapsed them to the 150px iframe default — the "weirdly short" players. Target both wrapped and bare embeds and give them a responsive 16:9 box. */ figure.kg-embed-card iframe[src*="youtube.com/embed"], figure.kg-embed-card iframe[src*="player.vimeo.com"], article iframe[src*="youtube.com/embed"], article iframe[src*="youtube-nocookie.com/embed"], article iframe[src*="player.vimeo.com"] { width: 100%; height: auto; aspect-ratio: 16/9; display: block; margin: 1.5rem auto; } figure.kg-bookmark-card { position: relative; display: flex; margin: 1.5rem 0; border: 1px solid var(--border); border-radius: 8px; overflow: hidden; min-height: 116px; } .kg-bookmark-container { position: absolute; inset: 0; z-index: 1; } .kg-bookmark-content { padding: 1rem 1.1rem; flex: 1 1 auto; min-width: 0; } .kg-bookmark-title { font-weight: 600; color: var(--fg); } .kg-bookmark-description { color: var(--muted); font-size: .9rem; margin-top: .4rem; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; } .kg-bookmark-metadata { margin-top: .6rem; font-size: .8rem; color: var(--muted); display: flex; align-items: center; gap: .5rem; } .kg-bookmark-icon { width: 18px; height: 18px; border-radius: 3px; background: none; animation: none; } .kg-bookmark-thumbnail { flex: 0 0 34%; max-width: 200px; } .kg-bookmark-thumbnail img { width: 100%; height: 100%; object-fit: cover; display: block; border-radius: 0; } @media (max-width: 480px) { .kg-bookmark-thumbnail { display: none; } } /* Internal "embed the post" card — replaces a tweet embed that's also a post here. */ figure.post-embed-card { display: flex; margin: 2rem 0; } .pec-link { display: flex; flex: 1; min-width: 0; text-decoration: none; overflow: hidden; border: 1px solid var(--border); border-radius: 8px; min-height: 96px; transition: border-color .1s; } .pec-link:hover { border-color: var(--accent); } .pec-content { padding: 1rem 1.1rem; flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; justify-content: center; } .pec-label { font-size: .72rem; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); margin-bottom: .35rem; } .pec-title { font-weight: 600; color: var(--fg); line-height: 1.3; } .pec-thumb { flex: 0 0 34%; max-width: 200px; } .pec-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; } @media (max-width: 480px) { .pec-thumb { display: none; } } article h2 { margin-top: 2rem; } article pre { background:var(--code-bg); padding:1rem; overflow:auto; border-radius:6px; } article code { background:var(--code-bg); padding:.15em .35em; border-radius:3px; font-size:.9em; } article pre code { background:none; padding:0; } article blockquote { border-left:3px solid var(--border); margin:1.5rem 0; padding:.2rem 0 .2rem 1rem; color:var(--quote); } article table { border-collapse: collapse; width: 100%; margin: 1.5rem 0; font-size: .92em; display: block; overflow-x: auto; } article th, article td { text-align: left; padding: .5rem .7rem; border-bottom: 1px solid var(--border); white-space: nowrap; } article th { font-weight: 700; color: var(--heading); border-bottom: 2px solid var(--border); } article tbody tr:hover { background: var(--code-bg); } article tr.exited-row { background: #dcfce7; } article tr.exited-row:hover { background: #c7f0d6; } html[data-theme="dark"] article tr.exited-row { background: #14321f; } html[data-theme="dark"] article tr.exited-row:hover { background: #1b432a; } article blockquote > :first-child { margin-top:0; } article blockquote > :last-child { margin-bottom:0; } .backlink { display:inline-block; margin-bottom:2rem; color:var(--muted); } /* Post page top row: backlink left, edit button pushed to the right. */ .post-topbar { display:flex; justify-content:space-between; align-items:baseline; gap:1rem; } .post-topbar .backlink { margin-bottom:2rem; } .edit-link { color: var(--accent); font-size: .82em; white-space: nowrap; } ul.posts li .edit-link { flex: none; margin-left: auto; } ul.posts li .from-x ~ .edit-link { margin-left: .5rem; } footer.site { margin-top: 4rem; color: var(--muted); font-size: .85rem; } .site-search { margin: 0 0 1rem; } /* Same visual language as the newsletter email box: white field, blue ring, soft shadow (email box keeps its flat right edge for the button). */ .site-search input { width: 100%; padding: 19px; font-size: 15px; color: #3c484e; background: #fff; border: none; border-radius: 6px; outline: none; box-shadow: 0 0 0 1px var(--accent), 0 2px 4px 0 rgb(0 0 0 / 7%), 0 1px 1.5px 0 rgb(0 0 0 / 5%); } .site-search input:focus { box-shadow: 0 0 0 2px var(--accent), 0 2px 4px 0 rgb(0 0 0 / 7%); } /* Dark mode: dark field, quiet border (the white field + blue ring glared). */ html[data-theme="dark"] .site-search input { background: var(--bg); color: var(--fg); box-shadow: 0 0 0 1px var(--border); } html[data-theme="dark"] .site-search input:focus { box-shadow: 0 0 0 1px var(--accent); } .topnav { color: var(--muted); font-size: .85rem; margin: 0 0 1.6rem; } .search-sort { color: var(--muted); font-size: .85rem; margin-top: .5rem; } .search-sort-filter { margin-top: .15rem; } .search-sort strong { color: var(--fg); } ul.search-results { list-style: none; padding: 0; margin: 1.5rem 0 0; } ul.search-results li { margin: 0 0 1.5rem; } ul.search-results li a { font-weight: 700; } ul.search-results time { display: block; color: var(--muted); font-size: .8rem; margin-top: .15rem; } .snippet { color: var(--muted); margin: .3rem 0 0; font-size: .92rem; } .site-credit { margin-top: .8rem; font-size: .8em; color: var(--muted); } .post-source { margin-top: 2rem; color: var(--muted); } .post-ps { margin-top: 0; color: var(--fg); } hr { background-color: var(--hr); height: 1px; width: 100%; border-style: none; margin: 28px 0; } .post-nav { display: flex; justify-content: space-between; gap: 1.5rem; } .post-nav .prev, .post-nav .next { flex: 0 1 50%; } .post-nav .next { margin-left: auto; } .post-nav a { font-weight: 700; display: flex; align-items: center; gap: .4em; } .post-nav .next a { justify-content: flex-end; } .post-nav .chev { flex: none; font-size: 1.4em; color: inherit; line-height: 1; } .post-nav .t { text-align: center; } /* Pie chart of category breakdown (SVG with hoverable slices) */ .projects-pie-wrap { display: flex; justify-content: center; margin: 1.5rem 0; } .projects-pie { width: 380px; height: 290px; max-width: 100%; filter: drop-shadow(0 1px 4px rgba(0,0,0,.1)); } html[data-theme="dark"] .projects-pie { filter: drop-shadow(0 1px 6px rgba(0,0,0,.4)); } .projects-pie path, .projects-pie circle { cursor: default; transition: opacity 80ms; } .projects-pie path:hover, .projects-pie circle:hover { opacity: .85; } .pie-label { fill: var(--fg); font-size: 9px; font-weight: 600; dominant-baseline: central; pointer-events: none; } .pie-lead { stroke: var(--muted); stroke-width: 0.6; fill: none; } .chart-legend { display: flex; flex-wrap: wrap; gap: .5rem 1rem; margin-top: .8rem; font-size: .85em; color: var(--muted); } .chart-legend .legend-item { display: inline-flex; align-items: baseline; gap: .35rem; white-space: nowrap; } /* /ask-test chat UI */ .ask-form { display: flex; gap: .5rem; margin: 1rem 0 .5rem; } .ask-form input { flex: 1; padding: .6rem .8rem; font-size: 1rem; border: 1px solid var(--border); border-radius: 8px; background: var(--bg); color: var(--fg); font-family: inherit; } .ask-form input:focus { outline: 2px solid var(--accent); outline-offset: 1px; } .ask-form button { padding: .6rem 1.2rem; font-size: 1rem; font-weight: 700; background: var(--accent); color: #fff; border: none; border-radius: 8px; cursor: pointer; font-family: inherit; } .ask-form button:disabled { opacity: .5; cursor: wait; } .ask-suggest { font-size: .85em; color: var(--muted); margin-bottom: 1.2rem; } .ask-out { font-size: 1rem; line-height: 1.65; padding: 1rem 1.2rem; background: var(--code-bg); border-radius: 10px; min-height: 3rem; display: none; } .ask-out.show { display: block; } .ask-out.loading::before { content: 'Thinking'; color: var(--muted); font-style: italic; } .ask-out.loading::after { content: '...'; color: var(--muted); font-style: italic; display: inline-block; width: 0; overflow: hidden; vertical-align: bottom; animation: think-dots 1.4s steps(4, end) infinite; } @keyframes think-dots { to { width: 1.6em; } } .ask-out.error { background: rgba(239,68,68,.1); color: #b91c1c; } .ask-meta { font-size: .75em; color: var(--muted); margin-top: .6rem; padding-top: .6rem; border-top: 1px dashed var(--border); } /* Mobile: stop the page from gaining horizontal scroll, without forcing the .wrap container to 100% (which broke the centered 42rem desktop layout). iOS Safari sometimes ignores overflow-x on body alone — set it on html too. */ html, body { overflow-x: hidden; max-width: 100%; } /* Belt-and-suspenders: clamp every chart container to its parent so an oversized SVG / inline-styled bar can't push the page wider on mobile. */ .projects-pie-wrap, .projects-pie, .projects-chart, .chart-bar, .projects-timeline, .tl-bars, .tl-axis, .projects-scoreboard { max-width: 100%; box-sizing: border-box; } /* Long project names / pasted URLs in notes were also a culprit. */ .proj-name, .proj-note, .scorecard-names { word-break: break-word; overflow-wrap: anywhere; } article img, article video, article iframe { max-width: 100%; height: auto; } article pre, article code { max-width: 100%; overflow-x: auto; word-wrap: break-word; } /* Cursor-following tooltip for SVG charts (pie slices have no ::after). */ .chart-tooltip { position: absolute; background: rgba(20,22,26,.92); color: #fff; padding: 5px 10px; border-radius: 6px; font-size: 12px; line-height: 1.4; white-space: nowrap; pointer-events: none; opacity: 0; transition: opacity 60ms; z-index: 1000; font-weight: 500; } /* Bar-segment instant tooltip (HTML so ::after works, like the timeline tiles). */ .bar-seg { position: relative; } .bar-seg::after { content: attr(data-tip); position: absolute; bottom: 100%; left: 50%; transform: translateX(-50%) translateY(-6px); background: rgba(20,22,26,.92); color: #fff; padding: 5px 10px; border-radius: 6px; font-size: 12px; line-height: 1.4; white-space: nowrap; pointer-events: none; opacity: 0; transition: opacity 60ms; z-index: 5; font-weight: 500; } .bar-seg:hover::after { opacity: 1; } /* /projects scoreboard + list */ .projects-scoreboard { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1rem; margin: 1.75rem 0 2.25rem; align-items: stretch; } .scorecard { padding: 1.2rem 1.2rem 1.1rem; border-radius: 12px; border: 1px solid rgba(0,0,0,.06); display: flex; flex-direction: column; } .scorecard.good { background: #dcfce7; } /* pastel green */ .scorecard.made { background: #fef3c7; } /* pastel yellow */ .scorecard.failed { background: #fee2e2; } /* pastel red */ .scorecard.nonprofit { background: #ede9fe; } /* pastel purple */ .scorecard.newproj { background: #dbeafe; } /* pastel blue */ .scorecard.exited { background: #fce7f3; } /* pastel pink (success-exit) */ /* If there's an odd number of cards, let the last one span both columns. */ .scorecard:last-child:nth-child(odd) { grid-column: 1 / -1; } html[data-theme="dark"] .scorecard { border-color: rgba(255,255,255,.06); } html[data-theme="dark"] .scorecard.good { background: #102b1f; } html[data-theme="dark"] .scorecard.made { background: #2b2616; } html[data-theme="dark"] .scorecard.failed { background: #2b1818; } html[data-theme="dark"] .scorecard.nonprofit { background: #1f1a2e; } html[data-theme="dark"] .scorecard.newproj { background: #11202e; } html[data-theme="dark"] .scorecard.exited { background: #2b1424; } .scorecard-head { font-size: .72em; color: rgba(0,0,0,.6); text-transform: uppercase; letter-spacing: .08em; font-weight: 700; } html[data-theme="dark"] .scorecard-head { color: rgba(255,255,255,.65); } .scorecard-count { font-size: 2.6em; font-weight: 800; line-height: 1; margin: .35rem 0 .15rem; color: rgba(0,0,0,.85); display: flex; align-items: baseline; gap: .4rem; } html[data-theme="dark"] .scorecard-count { color: rgba(255,255,255,.95); } .scorecard-pct { font-size: .42em; font-weight: 500; color: rgba(0,0,0,.5); letter-spacing: .02em; } html[data-theme="dark"] .scorecard-pct { color: rgba(255,255,255,.55); } .scorecard-names { margin-top: .9rem; padding-top: .9rem; border-top: 1px dashed rgba(0,0,0,.1); display: flex; flex-wrap: wrap; gap: .4rem; } html[data-theme="dark"] .scorecard-names { border-top-color: rgba(255,255,255,.1); } .name-pill { display: inline-block; padding: .22em .7em; border-radius: 999px; background: rgba(255,255,255,.75); color: rgba(0,0,0,.85); font-size: .82em; line-height: 1.5; border: 1px solid rgba(0,0,0,.06); text-decoration: none; font-weight: 500; transition: background 80ms, color 80ms, border-color 80ms; } html[data-theme="dark"] .name-pill { background: rgba(255,255,255,.08); color: rgba(255,255,255,.92); border-color: rgba(255,255,255,.06); } a.name-pill:hover { background: rgba(0,0,0,.85); color: #fff; border-color: transparent; text-decoration: none; } html[data-theme="dark"] a.name-pill:hover { background: rgba(255,255,255,.9); color: #15171a; } @media (max-width: 640px) { .projects-scoreboard { grid-template-columns: 1fr; } } .projects-list { margin-top: 1.5rem; } .proj-year { font-weight: 700; font-size: 1.25em; margin: 1.6em 0 .6em; } .proj-entry { display: flex; gap: .55rem; align-items: baseline; padding: .3rem 0; } .proj-date { color: var(--muted); font-variant-numeric: tabular-nums; width: 4.5em; flex: none; font-size: .9em; } .proj-name { font-weight: 700; } .proj-name a { color: var(--fg); } .proj-status { color: var(--muted); font-size: .82em; margin-left: auto; flex: none; } .proj-post { font-size: .9em; margin: -.15em 0 .6em 0; } .proj-post a { color: var(--accent); font-weight: 600; } .proj-note { color: var(--quote); font-size: .92em; margin: .15em 0 .5em 0; line-height: 1.55; } .cat-dot { font-size: 1.1em; line-height: 1; flex: none; } .cat-dot.good { color: #16a34a; } .cat-dot.made { color: #d97706; } .cat-dot.failed { color: #ef4444; } .cat-dot.nonprofit { color: #8b5cf6; } .cat-dot.newproj { color: #2563eb; } .cat-dot.exited { color: #ec4899; } /* 100%-stacked bar visualising the same breakdown */ .projects-chart { margin: 0 0 2rem; } .chart-bar { display: flex; height: 40px; border-radius: 8px; overflow: hidden; border: 1px solid rgba(0,0,0,.06); } html[data-theme="dark"] .chart-bar { border-color: rgba(255,255,255,.06); } .bar-seg { display: flex; align-items: center; justify-content: center; font-size: .82em; font-weight: 700; color: #fff; min-width: 2px; transition: opacity 80ms, transform 80ms; cursor: pointer; } .bar-seg:hover { opacity: .85; } .bar-seg.dim { opacity: .25; } .bar-seg.active { transform: scaleY(1.08); box-shadow: inset 0 0 0 2px rgba(255,255,255,.6); } /* Click-to-filter (multi-select): timeline gets `filtering` + a `sel-` class per chosen category; only those categories' tiles stay visible. Click a legend item or bar segment; shift-click to combine several. */ .projects-timeline.filtering .tl-seg { display: none; } .projects-timeline.filtering.sel-good .tl-seg.good { display: block; } .projects-timeline.filtering.sel-made .tl-seg.made { display: block; } .projects-timeline.filtering.sel-failed .tl-seg.failed { display: block; } .projects-timeline.filtering.sel-nonprofit .tl-seg.nonprofit { display: block; } .projects-timeline.filtering.sel-newproj .tl-seg.newproj { display: block; } .projects-timeline.filtering.sel-exited .tl-seg.exited { display: block; } .bar-seg.good { background: #16a34a; } .bar-seg.made { background: #d97706; } .bar-seg.failed { background: #ef4444; } .bar-seg.nonprofit { background: #8b5cf6; } .bar-seg.newproj { background: #2563eb; } .bar-seg.exited { background: #ec4899; } .chart-legend { display: flex; flex-wrap: wrap; gap: .5rem 1rem; margin-top: .8rem; font-size: .85em; color: var(--muted); } .legend-item { display: inline-flex; align-items: center; gap: .35rem; cursor: pointer; transition: opacity .1s; } .chart-legend.has-active .legend-item { opacity: .4; } .chart-legend.has-active .legend-item.active { opacity: 1; } .chart-legend .legend-item.active strong { text-decoration: underline; } .chart-hint { font-size: .8em; color: var(--muted); opacity: .7; margin: .5rem 0 0; } .legend-dot { display: inline-block; width: 10px; height: 10px; border-radius: 50%; box-shadow: inset 0 0 0 .5px rgba(0,0,0,.25); } .legend-dot.good { background: #16a34a; } .legend-dot.made { background: #d97706; } .legend-dot.failed { background: #ef4444; } .legend-dot.nonprofit { background: #8b5cf6; } .legend-dot.newproj { background: #2563eb; } .legend-dot.exited { background: #ec4899; } .legend-item strong { color: var(--fg); font-weight: 700; } /* Timeline (one column per year, stacked tiles per project) */ .projects-timeline { margin: 1.25rem 0 2rem; } .tl-controls { display: flex; gap: .8rem; align-items: center; margin: 0 0 .5rem; font-size: .85em; color: var(--muted); flex-wrap: wrap; } .tl-toggle { display: inline-flex; align-items: center; gap: .4em; cursor: pointer; user-select: none; } .tl-toggle input { cursor: pointer; margin: 0; } /* "Only show launches" hides every continuation tile (overrides filter rules too). */ .projects-timeline.launches-only .tl-seg:not(.tl-seg-launch) { display: none !important; } .tl-bars { display: flex; align-items: flex-end; gap: 1px; /* height set inline so PHP can scale to the busiest year */ border-bottom: 1px solid var(--border); padding: 0 0 1px; } .tl-col { flex: 1; display: flex; flex-direction: column-reverse; gap: 1px; min-width: 4px; height: 100%; justify-content: flex-start; } .tl-seg { flex: none; height: 4px; border-radius: 1px; display: block; position: relative; transition: filter 80ms; } /* Non-transforming hover so the child tooltip isn't stretched. */ .tl-seg:hover { filter: brightness(1.4) saturate(1.15); z-index: 2; box-shadow: 0 0 0 1px rgba(255,255,255,.4); } html[data-theme="dark"] .tl-seg:hover { box-shadow: 0 0 0 1px rgba(0,0,0,.4); } /* Instant tooltip on hover — native title has ~1s delay. */ .tl-seg::after { content: attr(data-tip); position: absolute; bottom: 100%; left: 50%; transform: translateX(-50%) translateY(-6px); background: rgba(20,22,26,.92); color: #fff; padding: 5px 10px; border-radius: 6px; font-size: 12px; line-height: 1.4; white-space: nowrap; pointer-events: none; opacity: 0; transition: opacity 60ms; z-index: 5; font-weight: 500; } .tl-seg:hover::after { opacity: 1; } .tl-seg.good { background: #16a34a; } .tl-seg.made { background: #d97706; } .tl-seg.failed { background: #ef4444; } .tl-seg.nonprofit { background: #8b5cf6; } .tl-seg.newproj { background: #2563eb; } .tl-seg.exited { background: #ec4899; } /* Revenue chart — same visual language as the timeline above it. */ .projects-revenue { margin: 1.25rem 0 2rem; } .rev-title { font-size: .85em; color: var(--muted); margin: 0 0 .5rem; } .rev-bars { display: flex; align-items: flex-end; gap: 1px; border-bottom: 1px solid var(--border); padding: 0 0 1px; } .rev-col { flex: 1; display: flex; flex-direction: column-reverse; gap: 0; min-width: 4px; height: 100%; justify-content: flex-start; } /* Faint inset outline keeps white/light segments (Merch, X) visible on light theme. */ .rev-seg { flex: none; display: block; border-radius: 1px; position: relative; transition: filter 80ms; box-shadow: inset 0 0 0 .5px rgba(0,0,0,.18); } html[data-theme="dark"] .rev-seg { box-shadow: inset 0 0 0 .5px rgba(255,255,255,.12); } .rev-seg:hover { filter: brightness(1.4) saturate(1.15); z-index: 2; box-shadow: 0 0 0 1px rgba(255,255,255,.4); } html[data-theme="dark"] .rev-seg:hover { box-shadow: 0 0 0 1px rgba(0,0,0,.4); } .rev-seg::after { content: attr(data-tip); position: absolute; bottom: 100%; left: 50%; transform: translateX(-50%) translateY(-6px); background: rgba(20,22,26,.92); color: #fff; padding: 5px 10px; border-radius: 6px; font-size: 12px; line-height: 1.4; white-space: nowrap; pointer-events: none; opacity: 0; transition: opacity 60ms; z-index: 5; font-weight: 500; } .rev-seg:hover::after { opacity: 1; } .tl-axis { display: flex; gap: 1px; font-size: .68em; color: var(--muted); margin-top: .4rem; } .tl-axis span { flex: 1; text-align: center; min-width: 4px; overflow: visible; white-space: nowrap; } /* When linked to from a pill, give the target row a subtle highlight + breathing room */ .proj-entry { scroll-margin-top: 1rem; transition: background 200ms; } .proj-entry:target { background: rgba(255,235,165,.35); border-radius: 6px; padding-left: .35rem; margin-left: -.35rem; } html[data-theme="dark"] .proj-entry:target { background: rgba(255,235,165,.08); } form.catch-emails { text-align: center; margin-top: 1.5rem; } /* Newsletter signup card on the front page, below the pinned box. */ .front-subscribe { margin: 1.5rem 0 0; padding: 1.25rem 1.25rem 1.5rem; border: 1px solid var(--border); border-radius: 8px; background: var(--code-bg); } /* Subscription-confirmed banner on top of the front page (/confirm). */ .confirm-banner { margin: 1rem 0 1.5rem; padding: 1rem 1.25rem; border: 2px solid var(--accent); border-radius: 8px; background: var(--code-bg); } .confirm-banner p { margin: .35em 0; } .front-subscribe p { margin: 0; text-align: center; color: var(--fg); } /* The lead line: inside the subscribe card on the blog, above it on the intro. */ .fs-lead { line-height: 1.45; text-wrap: balance; text-align: center; margin: 1.5rem 0 .75rem; color: var(--fg); } .fs-lead + .front-subscribe { margin-top: 0; } .front-subscribe .fs-note { max-width: 28rem; margin: .85rem auto 0; font-size: .8rem; color: var(--muted); line-height: 1.4; text-wrap: balance; } .front-subscribe form.catch-emails { margin-top: 1rem; margin-bottom: 0.5rem; } .catch-emails-text-box { outline:none; border:none; border-radius:6px; border-top-right-radius:0; border-bottom-right-radius:0; padding:12px; font-size:15px; vertical-align:middle; color:#3c484e; border-right:none; width:200px; box-shadow:0 0 0 1px var(--accent), 0 2px 4px 0 rgb(0 0 0 / 7%), 0 1px 1.5px 0 rgb(0 0 0 / 5%); } .catch-emails-button { cursor:pointer; background:var(--accent); appearance:none; color:#fff; padding:11px; font-weight:600; margin-left:0; font-size:15px; border-radius:6px; border-top-left-radius:0; border-bottom-left-radius:0; vertical-align:middle; border:1px solid var(--accent); z-index:1; position:relative; outline:1px solid var(--accent); width:130px; } .catch-emails-button:hover { background:none; color:var(--accent); border:1px solid var(--accent); } /* The intro keeps the chunkier 2px border it always had. */ #intro .catch-emails-button, #intro .catch-emails-button:hover { border-width:2px; } /* White on the accent measures 2.42:1 against the lighter dark-mode accent — well under the 4.5:1 AA wants for 15px/600 text. Dark text on the same blue is 7.41:1, so dark mode flips the label. */ html[data-theme="dark"] .catch-emails-button { color:#fff; } html[data-theme="dark"] .catch-emails-button:hover { color:var(--accent); } form.catch-emails .loading, form.catch-emails .error, form.catch-emails .success { display:none; } form.catch-emails.loading .not-subscribed, form.catch-emails.success .not-subscribed { display:none; } form.catch-emails.loading .loading { display:block; color:var(--muted); } form.catch-emails.error .error { display:block; color:#d33; margin-top:.5rem; } form.catch-emails.success .success { display:block; color:var(--accent); font-weight:600; } @media (max-width: 480px) { .catch-emails-text-box, .catch-emails-button { width:100%; margin:0 0 14px; display:block; border-radius:12px; } .catch-emails-button { margin:0; } } Subscribe
← all posts

In the future writing actual code will be like using a pro DSLR camera, and no code will be like using a smartphone camera

8 September, 2019 · 10,938 views

In the future writing actual code will be like using a pro DSLR camera, and no code will be like using a smartphone camera. Some pros will keep doing work with DSLRs (and need them), but most basic apps will be built with no code. Just like most photos now are shot on a phone.

"No code" means building applications without writing code, but instead using a visual platform to connect data and build interfaces. Kinda like Excel, but more visual like this, and it has an actual app/site as output instead of a spreadsheet:

And I think this means we'll see The Great Democratization of Software Creation, where most people can just glue some APIs together to build a functioning app/site/thing in hours/days. Kinda like website builders now, but with added API plugging in (like Zapier) and scheduled cron jobs.

That's why the complexity of current code frameworks is so interesting, it's essentially people buying expensive DSLRs with shitloads of lenses, light sensors, etc. which they may not really need. Especially not if their simple apps can be put together with no code.

Just like modern smartphone cameras are good enough (and are used) to make 99% of people's photos.

Towards 95% no code, 5% custom premium code

I think we'll see coding go in two directions:
1) towards simplicity with no code, for 95-99% of apps
2) towards complexity with custom high-level engineered code, for the 1-5% of apps
The engineers working on (2) will be high paid, in the millions/year, because custom code

A recent example is Netflix, who said they want to have LESS employees, paid higher. They'll pay $1M/y+ for 1 engineer instead of $300k/y for 3 engineers.

Current no code platforms

The major no code platforms currently are Bubble and Webflow. Website builders that could be used for no code (when combined with an API platform like Zapier) are Carrd, Squarespace and Wix. I'm not affiliated with any of these, except that Carrd's AJ is my Twitter friend!

What to build for this trend

1) build paid APIs that people in the future can use to build stuff
2) build no code platforms (many already exist though, but why not)
3) build no code tools (not sure what thought)  But note it might take 5-10 years for this to get going

I wouldn't stop at just building one API either, build a big set of APIs people making apps can use. For example, I use lots of APIs on Nomad List to get daily data, like Dark Sky for weather: darksky.net/dev

Collecting lots of dispersed data (for example from government) and merging it together and normalizing it into one format and then selling it for $$$ is a nice example.

Example from this week: there's no API to get neighborhood boundaries for cities worldwide, the data if even available is dispersed on national and city government sites. I'm collecting/drawing neighborhood boundaries in GeoJSON polygons for Hoodmaps that if I wanted I could put in an API later and maybe sell for $$$.

Conclusion

It's impossible to predict the future, but we can try. And the trend of no code while in its infancy now, will only continue I think. It's now at the state of interactive templates and connecting some basic logic. But it will advance more and means a serious overhaul of the software industry for majority of use cases.

Most software engineers will deny this trend though, but as my friend John from Ghost says:

Everyone can identify disruption until it’s about them

On levels.io
In the future writing code will be like using a pro dslr and no code like a smartphone camera

P.S. I'm on 𝕏 too if you'd like to follow more of my stories. And I wrote a book called MAKE about building startups without funding. See a list of my stories or contact me.

Never miss a post: 182,460 people read my posts in full by email, before it spreads anywhere else

Subscribing you...
Subscribed! Check your inbox to confirm your email.

Or subscribe on Telegram →


Related posts