πΊοΈ v0.24.4 β Maps, Geocoding & Address Autocomplete
Midwest gains a full map and geocoding stack: an interactive MapComponent, an address-autocomplete form field, and a server-side geocoding concern for your models β all wired through a single Midwest.configure block. Three providers are supported (open-source Leaflet + Nominatim, Apple MapKit JS, and Google Maps), and no external map script loads until you pick one, keeping the privacy-first default. Turbo page transitions and the newest social platforms round out the release.
What's New
MapComponent β Interactive maps, three providers, zero forced dependencies
midwest_map renders an interactive map with a graceful placeholder that shows until a provider is configured β nothing is fetched from Apple, Google, or OpenStreetMap until you opt in.
<%= midwest_map(latitude: 37.334886, longitude: -122.008988) %><%= midwest_map( latitude: 41.8826, longitude: -87.6233, span: 0.05, map_type: :muted, markers: [ Midwest::Map::MarkerData.new( latitude: 41.8826, longitude: -87.6233, title: "Millennium Park", open: true ) ] ) %>Pick a provider once and every map is wired automatically:
# Open-source β no API keyMidwest.configure { |c| c.map.provider = :leaflet }# Apple MapKit JSMidwest.configure do |c| c.map.provider = :mapkit c.map.token_url = "/mapkit_token" || ENV.fetch("GOOGLE_MAPS_API_KEY")end# Google MapsMidwest.configure do |c| c.map.provider = :google c.map.api_key = ENV.fetch("GOOGLE_MAPS_API_KEY")endFour map types (:muted, :standard, :hybrid, :satellite), auto/light/dark color schemes, marker annotations (Midwest::Map::MarkerData β or plain hashes, both coerced), zoom/compass/user-location controls, and callouts that can open on load. With :leaflet, map types map to free OpenStreetMap/CartoDB/ESRI tile layers and dark schemes switch to CartoDB Dark Matter automatically.
Form::AddressComponent β Address autocomplete with geocoding
f.address_field (or the standalone midwest_form_address) renders a text input backed by MapKit or Google Places autocomplete. Selecting a suggestion fills hidden latitude, longitude, and place-ID fields automatically; editing the text clears them, so a simple presence validation enforces "must pick from the list."
<%= form.address_field :location, show_map: true, map_height: 280 %>The geocoder backend is independent from the map renderer, so you can pair open-source Leaflet tiles with Apple geocoding, or run a fully key-free :leaflet + :nominatim stack. Custom geocode column names, country-code restrictions, and an optional inline map preview after selection are all supported.
Midwest::Geocodable β Server-side geocoding for your models
Include the concern, call the geocode macro once, and the configured backend populates latitude, longitude, place ID, and the raw provider response before validation whenever the address changes.
class Venue < ApplicationRecord include Midwest::Geocodable geocode :addressendCustom column names, per-model provider overrides, conditional geocoding (if:), a skip_geocoding flag, and a manual venue.geocode! are all available. Three backends ship β Nominatim (free, no key), MapKit, and Google β via Midwest::Geocoders.call, each returning a uniform Result. Geocoding failures are logged and swallowed so they never raise inside a save. Midwest::MapKit.token generates the ES256 JWT MapKit JS requires, and Midwest::MapKit::Authenticatable provides a self-refreshing token endpoint for a Rails controller.
PageTransitionComponent β Turbo view transitions
midwest_page_transition, placed once in your root layout, enables smooth Turbo navigations via the View Transitions API. Its Stimulus controller injects the <meta name="view-transition"> tag Turbo 8 requires and scopes the CSS animations to <html>.
<%= midwest_page_transition(style: :slide, direction: :left) %><%= tag.h1(**midwest_page_transition_name(:page_title)) %>Five styles β fade (default), slide (with direction:), scale, and none β all respect prefers-reduced-motion and resolve timing through the shared motion tokens. midwest_page_transition_name marks elements that should morph (rather than crossfade) between pages.
Social Preview β Threads & Bluesky
Two newer text-based platforms join the SocialPreview family. ThreadsComponent (wordmark header, thread-line option, reply/repost/like/send actions, optional engagement summary) and BlueskyComponent (sky-blue wordmark, inline per-action counts, link preview card, quote-post support) β both wired into the parent orchestrator and Lookbook previews.
What Changed
Form::LiveSummaryComponent β Address-aware rows
The live summary now understands address fields. It listens for the midwest-address:geocode event and renders the selected address as a plain-text row whose value, on hover, reveals a compact popup with the resolved latitude, longitude, and place ID. The combobox input and its hidden geocode inputs are collapsed into that single row rather than listed separately.
PopoverComponent β Animated close via new Stimulus controller
A new midwest-popover Stimulus controller plays the motion exit animation before the native popover close fires. This fixes a Chromium quirk where removing :popover-open recalculated position-try-fallbacks and visibly jumped the popover to a different anchor while it was still on screen β the recalculation now happens while the element is already transparent.
OnboardingComponent β Popover confirmation & motion replay on step change
The "exit the tour?" prompt now uses the popover-based midwest_confirmation dialog instead of a blocking window.confirm() call. Step transitions also re-trigger the shared motion system's @starting-style entry so each card animates in as you move through the tour, and any open confirmation popovers are dismissed before the card hides.
What Was Removed
OnboardingComponentconfirm:data value β themidwest-onboarding-confirm-valuestring and itswindow.confirm()code path were removed in favor of the popover-based confirmation dialog. Exit confirmation is now wired through the confirmation component; no consumer-facing option changed.- Nothing else was removed. There are no other breaking API changes this release.
Dev Process
- Install generators β
lib/install/maps_setup.rbscaffolds map/geocoder configuration, with anaddress.rbmodel template and an updatedmidwest.rbinitializer template. Covered by newinstall_test.rbandmaps_setup_test.rbsuites. - Configuration β new
config.map,config.geocoder, andconfig.page_transitionblocks onMidwest::Configuration, each with independent provider selection and sensible fallbacks (geocoder infers:nominatimwhen the map provider is:leaflet). - JavaScript β a
map_providers.tsmodule handles per-provider CDN loading with script deduplication so multiple maps mounting at once never inject duplicate scripts. - Tests β new suites for
Geocodable,Geocoders,MapComponent,Map::MarkerData,Form::AddressComponent,PageTransitionComponent, and the map install path (β2,400 lines added). - Docs β two new reference pages: Motion Design and Maps & Geocoding, the latter documenting the three-provider matrix and mix-and-match renderer/geocoder setups.
- Gemfile locks β routine lock bumps for the Rails 7, Rails 8, and Bridgetown v1/v2 gemfiles.