Adam Argyle

Adam Argyle

By
Download on the App Store

Adam Argyle episodes

  • WWW Ep232 No Ralph Wiggum No Training Wheels Just Agents

    Ep #232

    No Ralph Wiggum. No Training Wheels. Just Agents.

    • do Ralph Wiggum loops automatically produce better results?
    • is context pollution killing productivity?
    • the rise and fall of MCP servers and skills
    • why vanilla Claude might be the only tool you actually need
    • CSS anchor positioning failures
    • Stranger Things hot takes
    • why small distilleries are just like indie startups
    • ⤷ whiskey.fm · youtube · spotify · apple

      0 min
    • WWW 231 We Stopped Coding By Hand

      Ep #231

      We stopped coding by hand because agentic AI is too good

      Robbie and Chuck dig into agentic AI loops, the rise of prompt-driven development, whether coding by hand is officially dead, and what happens when tools like OpenCode and Claude become the new normal.

      ⤷ whiskey.fm · youtube · spotify · apple

      0 min
    • WWW Ep230 We Accidentally Deleted Programming

      Ep #230

      We accidentally deleted programming

      Robbie and I dig into the shift from code-first to spec-first development, whether TypeScript still matters in an AI-driven world, the rise of agency over specialization, and why the future might belong to prompt-driven tinkerers instead of traditional developers.

      ⤷ whiskey.fm · youtube · spotify · apple

      0 min
    • Nice Select

      This post pushes customizable to an extreme. I think it turned out nice.

      It's mostly CSS! A li'l JS sprinkle just to position the ::picker() and the selected option together; the JS is nice to have, CSS does the real work.
      superellipse forced colors light/dark theme slick scrollbar scroll driven animations scroll-state stuck query scroll-state scrollable query overscroll behavior smooth scroll RTL support text-box trim spring easing anchor & anchor fallbacks color-mix()
      Chrome only atm, and I keep mobile with the native select. Still cool!
      Try it
      Core Architecture
      #
      This component builds on the new appearance: base-select foundation, combining native accessibility with sweet visual control. Progressive enhancement ensures the custom experience only loads on capable devices.
      The examples in this post just look like regular select elements in Firefox and Safari. Firefox is working on it though!
      appearance: base-select
      unlocks full customization of native elements while retaining browser controlled accessibility and keyboard navigation

    • Conditional enhancement
    • Only enables custom styling for devices with hover and fine pointer (@media (hover) and (pointer: fine))
    • JavaScript + CSS hybrid
    • JS calculates an anchor offset for alignment while CSS handles all visual presentation and animation
      Alignment & Positioning
      #

      Anchor positioning plus some special JS sauce to write an inline CSS variable unlocks the picker to position near the selected option (obviously this can't work at the viewport edges). This creates a pleasant morphing effect that feels connected.

      Maybe I should have used AIM?

      • CSS Anchor Positioning
      • Picker uses anchor(start) to position relative to the trigger button
      • Selected option alignment
      • When dropdown opens, the currently selected option aligns vertically with the button text via --_select-anchor-offset custom property
      • Syncs
      • transform-origin
        With 50% var(--_select-anchor-offset) ensures scale animations line up better with the selected option's position
      • Anchor fallbacks with
      • position-try
        flip-block, flip-inline automatically repositions the anchor position if the picker would overflow the viewport

        Animation & Transitions
        #

        Scale effects with spring physics to scroll-driven option reveals, every interaction respects user motion preferences while providing rich feedback.

        • @starting-style
        • Defines entry animation initial state for the popover
        • Spring easing
        • Uses Open Props --ease-spring-3 for playful, physics-based motion on scale and rotation
        • Discrete property transitions
        • display and overlay use allow-discrete for proper ::picker() popover animation support
        • Chevron rotation
        • ::picker-icon rotates 180° on open with spring easing
        • Scroll-driven option fade-in
        • Options animate in from the bottom using animation-timeline: view()
        • Respects reduced motion
        • Animations and transitions wrapped in @media (prefers-reduced-motion: no-preference)
        • Button press feedback
        • Active state scales down to 0.98 for tactile feedback
        • Open state button feedback
        • Select scales up to 1.04 when :open, scaling back down on close with a soft bounce

          Theming & Color Handling
          #

          Automatic light/dark adaptation leverages system colors and modern color functions for seamless theme switching. Definitely check the support for light/dark forced colors too, a really great way to debug spacing and skeletal aspects of the UI.

          • color-scheme: light dark
          • Enables automatic system color adaptation
          • light-dark() function
          • Used for backgrounds that need different light/dark values
          • System colors
          • Canvas, CanvasText, Highlight, HighlightText for semantic, colors
          • color-mix()
          • Creates semi-transparent overlays and borders
          • Data URI icons
          • Chevron and checkmark icons embedded as SVG data URIs with variants for each color scheme
          • Forced colors support
          • @media (forced-colors: active) provides high-contrast mode adaptations (even the sticky scroll state respects this!)

            Layout & Spacing
            #

            Logical properties enable true internationalization with automatic RTL support, while text-box trim delivers pixel-perfect vertical alignment. Custom properties create a flexible design token system.

            • CSS Custom Properties design tokens
            • All spacing, sizes, and timing values defined as --_select-* variables
            • Logical properties throughout
            • inline-size, block-size, inset-block-start, margin-inline, padding-block for full RTL support
            • text-box: trim-both cap alphabetic
            • Precise vertical text alignment in legends
            • Flexible option content
            • Options use flexbox with gap for icon + text layout
            • Checkmark auto-positioned
            • order: 2 and margin-inline-start: auto pushes checkmark to end of option

              Scrolling & Overflow
              #

              Scroll-state queries dynamically detect when content is scrollable and when sticky headers become stuck. Custom scrollbar styling and smooth scrolling enhance the browsing experience within long option lists.

              • Container scroll-state queries
              • container-type: scroll-state enables @container scroll-state(stuck) detection
              • Sticky legends
              • Group headers stick to top when scrolling, with color change when stuck
              • Custom scrollbar styling
              • scrollbar-width: thin and scrollbar-color with fade on hover-out
              • Scroll containment
              • overscroll-behavior-block: contain prevents scroll chaining
              • Smooth scroll
              • scroll-behavior: smooth for programmatic scrolling (motion-safe)
              • Dynamic scrollable detection
              • JS adds .scrollable class when content exceeds max height, CSS adjusts padding accordingly

                Customizable Select Features
                #

                New pseudo-elements provide surgical styling control over picker components, while superellipse corners deliver the modern aesthetic popularized by iOS. State selectors enable precise targeting of open and selected states.

                • ::picker(select)
                • Pseudo-element for styling the dropdown picker container
                • ::picker-icon
                • Pseudo-element for the dropdown chevron indicator
                • ::checkmark
                • Pseudo-element for the selected option indicator

                • Native element that mirrors selected option content in the button
                • Squircle corners
                • corner-shape: superellipse(1.25) for modern rounded rectangles (with fallback)
                • :open pseudo-class
                • State selector for when picker is visible
                • :checked on options
                • State selector for selected option

                  Accessibility & UX
                  #

                  Accessibility is built-in with proper focus indicators, keyboard navigation, and semantic markup thanks to the web platform. Touch-friendly targets and overflow handling ensure the component works elegantly across all input methods.

                  • Focus ring styling
                  • Distinct :focus (subtle) and :focus-visible (prominent)
                  • Disabled option support
                  • option[disabled] styling and hover prevention
                  • Text overflow handling
                  • text-overflow: ellipsis in button display
                  • Minimum touch target
                  • 36px item height matches mobile guidelines
                  • Hidden option support
                  • JS skips [hidden] options in offset calculations
                  • RTL example included
                  • Arabic language demo validates bidirectional layout
                    Performance Optimizations
                    #

                    Careful optimization strategies keep animations smooth while minimizing layout thrash. Smart caching and compositor hints ensure the component performs well even on lower-end devices.

                    • will-change: scale
                    • Hints to browser for compositor optimization (also fixes text shift bug)
                    • WeakMap offset cache
                    • Avoids expensive DOM measurements on every interaction;
                      auto-garbage-collected
                    • Hardcoded layout constants
                    • Avoids getComputedStyle forced reflows during initialization. Tried dynamically computing these in a hidden instance, but couldn't get it as close as just "hardcoding / speficying them ahead of time" in a config
                    • Background clip
                    • background-clip: padding-box prevents background bleeding under border
                    • Parallel icon variants
                    • Pre-defined black/white SVGs avoid runtime color manipulation
                      Example Variations
                      #

                      The component architecture supports diverse use cases, from simple toggles to rich content pickers. Each variation demonstrates how the base patterns adapt to different content and interaction models.

                      • Toggle with status indicators
                      • Colored dots (green/red/gray) for on/off/disabled states
                      • Avatar select
                      • Circular images with person names
                      • Multi-line options
                      • Title + description layout for status picker
                      • Grouped options
                      • Fieldsets with sticky legend headers
                      • Label above value
                      • Color space picker with small label text above the selected value
                      • Flag emoji select
                      • Country picker organized by region
                        Try It
                        #

                        Experience the component in action on CodePen:

                        See the Pen <a href="https://codepen.io/argyleink/embed/preview/019c1f28-bbc2-7bac-ad4a-a7e41d3730f1"> by Adam Argyle (<a href="https://codepen.io/argyleink">@argyleink</a>)
                        on <a href="https://codepen.io">CodePen</a>.

                        Wrapping Up
                        #

                        There's room for improvement, please fork and offer feedback!

                        I tried many variants of animation, trying to reduce some of the shift of the positioning, but never found something that worked across all the variants. Help me?! 🙏

                        This select component intends to showcase what's possible when we combine modern CSS features with thoughtful progressive enhancement. The appearance: base-select property provides the foundation, while anchor positioning, scroll-state queries, and entrance animations create a polished, accessible experience.

                        The real power lies in how these features compose together—each enhancement builds on platform primitives rather than fighting against them. The result is a component that's maintainable, accessible, and ready for the next generation of web interfaces.

                        Most importantly, this approach lets us create custom experiences without sacrificing the keyboard navigation, focus management, and screen reader support that come free with native elements.

                        For more, checkout nice details!

                        0 min
                      • March Mad CSS Md

                        This week I compete in Syntax's March madCSS; me against 16 other badass devs in CSS & UI challenges 😅

                        Wish me luck 🤞

                        0 min
                      • WWW Ep229 Is Ai Just Stealing With Extra Steps

                        Ep #229

                        Is AI just stealing with extra steps?

                        Robbie and I talk about the ethics and economics of AI, and why “stealing with extra steps” feels like the right metaphor for where things are headed.

                        We also dig into open source burnout, whether AI replaces developers or just reshapes the job, and some whatnot over whiskey.

                        ⤷ whiskey.fm · youtube · spotify · apple

                        0 min
                      • Anchor Interpolated Morph (AIM)

                        It's more natural when animations start near the trigger point, this CSS technique makes it easy. AIM animates from any element, and to any element.

                        Thanks to anchor(), anchor-size(), @starting-style, and interpolate-size.

                        All together they can create an anchor interpolated morph: an interruptible contextual transition from where a user invoked it.

                        That's a popover=hint + interestfor,
                        no JS CSS transition. Try it

                        This is a very powerful and capable feature combination.

                        Similar results are visually possible with child elements using their parent as the anchor, but:

                        1. What if they're not ancestors?
                        2. What if one is in the :top-layer?
                        3. What if you want semantic HTML and a healthy accessibility OM?
                        4. AIM is the remedy.

                          FLIP
                          #

                          This technique is inspired by Paul Lewis's coined animation technique called FLIP (First, Last, Invert, Play).

                          The gist of FLIP is to use JS to calculate the difference in positions/size/whatever of an element, before and after some DOM change.

                          AIM
                          #

                          The gist of AIM is to use CSS to access properties from where another element is (first), to where natural render is (last), and let transition handle invert and play.

                          First: anchor(), anchor-size() & @starting-style

                          Last: interpolate-size: allow-keywords & auto

                          The roles where JS was accessing properties, measuring natural sizes, and calculating the differences… are now handled by CSS. Definitely not 100% like FLIP 😅 but hopefully you see the connection. Jhey saw the connection too 🤘🏻 He sent me this demo which breaks down the basics really well too.

                          Here's a less designed example (so there's less CSS to sift through) that's a

                          element:

                          Try it. You can uncomment a top-left position example too!

                          How it works
                          #

                          1: The anchor-name property is used to identify the element that the element is morphing from.

                          button {
                          anchor-name: --⚓︎-morph;
                          }

                          Can you believe this one line of CSS enables any other element to observe the height, width, and position of this element 🤯

                          Want more about that emoji naming convention?

                          2: Link up the dialog with that anchor.

                          dialog {
                          position-anchor: --⚓︎-morph;
                          /* adjust position as desired */
                          left: anchor(left);
                          top: anchor(top);
                          }

                          3: Enable transitioning to auto width/height.

                          dialog {
                          interpolate-size: allow-keywords;
                          }

                          4: Enable transitions for all properties you want in the morph.

                          dialog {
                          @media (prefers-reduced-motion: no-preference) {
                          transition:
                          display var(--_speed) allow-discrete,
                          overlay var(--_speed) allow-discrete,
                          height var(--_speed) var(--ease-3),
                          width var(--_speed) var(--ease-3),
                          left var(--_speed) var(--ease-3),
                          top var(--_speed) var(--ease-3);
                          }
                          }

                          5: Now we can use @starting-style to apply the styles from the anchor to the dialog before it opens. I call this "enter stage from" styles, and you can apply as many of the anchor values as you want.

                          I specify all corners here and setup a mask system to allow the dialog to transition to fit it's content, from the anchor size, without distorting the dialog contents.

                          dialog {
                          @starting-style {
                          &[open] {
                          left: anchor(left);
                          top: anchor(top);
                          right: anchor(right);
                          bottom: anchor(bottom);
                          width: anchor-size(width);
                          height: anchor-size(height);
                          }
                          }
                          }

                          Notice anchor-size() for height and width, this combined with height and width auto and interpolate-size: allow-keywords will allow the dialog to transition to its natural size from the anchor with a nice reveal.

                          And for exit out on the dialog, it's similar as enter stage in my example but you could easily transition a different way or to a different element.

                          dialog {
                          &:not([open]) {
                          left: anchor(left);
                          top: anchor(top);
                          right: anchor(right);
                          bottom: anchor(bottom);
                          width: anchor-size(width);
                          height: anchor-size(height);
                          }
                          }

                          That's it for a dialog!

                          See the Pen <a href="https://codepen.io/argyleink/embed/preview/yyJaMaG"> by Adam Argyle (<a href="https://codepen.io/argyleink">@argyleink</a>)
                          on <a href="https://codepen.io">CodePen</a>.

                          AIM can be used for:

                          • Dialogs
                          • Popovers
                          • Tooltips
                          • Page preview
                          • Fullscreen takeover (go big!)
                          • Quick actions
                          • Disclosures
                          • Submenus
                          • Hovercards
                          • Particles? add some CSS random()
                          • Other overlay components?
                          • Here's the popover from the video at the beginning. It's using all the above techniques, plus some extras, precise timings, easings and intentionality.

                            See the Pen <a href="https://codepen.io/argyleink/embed/preview/PwzmJjj"> by Adam Argyle (<a href="https://codepen.io/argyleink">@argyleink</a>)
                            on <a href="https://codepen.io">CodePen</a>.

                            View Transitions
                            #

                            What about view transitions and morphing, don't they do that too?

                            Yep! They do, and can do some neat morphing tricks. Here's a view transitions demo I called "anything to anything".

                            See the Pen <a href="https://codepen.io/argyleink/embed/preview/qEWeYzd"> by Adam Argyle (<a href="https://codepen.io/argyleink">@argyleink</a>)
                            on <a href="https://codepen.io">CodePen</a>.

                            But… that requires JS, is always a straight line and is not elegantly interruptible.

                            "always straight lines"… once you notice it you can't unsee it. View transitions can't curve or swing to a new destination.

                            Here's some slides I made using the Astro Morphull repo I made that use no JS view transitions so content morphs: https://cascadiajs-2025.netlify.app.

                            Spatial Continuity
                            #

                            Traditional transitions often appear out of thin air or fade in from a generic center point. By leveraging anchor() and @starting-style, we can let CSS tell a story about where an element came from.

                            We're telling a story about where an element came from and why it’s there.

                            Go forth and make more natural transitions that feel spatially contextual. Stop letting your elements "teleport" into view, start letting them grow, stretch, and evolve directly from the components that triggered them.

                            Your users (and their muscle memory) will thank you. Speaking of, don't forget to gate the motion behind a prefers-reduced-motion media query (test the dialog and popovers).

                            Hot tip: You could easily morph to a different element than the invoking element, anchor() doesnt care.

                            Summary checklist for AIM:

                            1. Set the Anchor:
                              anchor-name on the invoking element to create a spatial reference.
                            2. Set "Enter From" and "Exit To" styles with anchor():
                              Use @starting-style to declare the entry and exit properties relative to the anchor.
                            3. Transition:
                              Orchestrate transition properties to allow the browser to interpolate the path, shapes and colors between the anchor point and the final layout.
                            4. 0 min

                            About Adam Argyle

                            From the publisher's feed

                            RSS Feed for Adam Argyle: Web design & development tips & tricks: CSS, JS, HTML, Design, & UX.