designmd-forge 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +119 -0
- package/dist/__tests__/designStore.test.js +41 -0
- package/dist/__tests__/linter.test.js +23 -0
- package/dist/__tests__/sectionParser.test.js +60 -0
- package/dist/__tests__/server.test.js +129 -0
- package/dist/constants.js +12 -0
- package/dist/data/authoring-guide.md +113 -0
- package/dist/data/designs/airbnb.md +545 -0
- package/dist/data/designs/airtable.md +554 -0
- package/dist/data/designs/apple.md +562 -0
- package/dist/data/designs/binance.md +634 -0
- package/dist/data/designs/bmw-m.md +503 -0
- package/dist/data/designs/bmw.md +544 -0
- package/dist/data/designs/bugatti.md +454 -0
- package/dist/data/designs/cal.md +542 -0
- package/dist/data/designs/card-heavy.md +250 -0
- package/dist/data/designs/claude.md +589 -0
- package/dist/data/designs/clay.md +541 -0
- package/dist/data/designs/clickhouse.md +544 -0
- package/dist/data/designs/cohere.md +451 -0
- package/dist/data/designs/coinbase.md +570 -0
- package/dist/data/designs/composio.md +506 -0
- package/dist/data/designs/content-first.md +236 -0
- package/dist/data/designs/cursor.md +537 -0
- package/dist/data/designs/data-dashboard.md +300 -0
- package/dist/data/designs/dell-1996.md +632 -0
- package/dist/data/designs/elevenlabs.md +504 -0
- package/dist/data/designs/enterprise-neutral.md +252 -0
- package/dist/data/designs/expo.md +526 -0
- package/dist/data/designs/ferrari.md +531 -0
- package/dist/data/designs/figma.md +578 -0
- package/dist/data/designs/framer.md +544 -0
- package/dist/data/designs/hashicorp.md +575 -0
- package/dist/data/designs/hp.md +670 -0
- package/dist/data/designs/ibm.md +550 -0
- package/dist/data/designs/intercom.md +546 -0
- package/dist/data/designs/ios-acorns-android.md +430 -0
- package/dist/data/designs/ios-acorns-expo.md +381 -0
- package/dist/data/designs/ios-acorns-swiftui.md +378 -0
- package/dist/data/designs/ios-acorns.md +384 -0
- package/dist/data/designs/ios-airbnb-android.md +658 -0
- package/dist/data/designs/ios-airbnb-expo.md +457 -0
- package/dist/data/designs/ios-airbnb-swiftui.md +464 -0
- package/dist/data/designs/ios-airbnb.md +410 -0
- package/dist/data/designs/ios-alltrails-android.md +451 -0
- package/dist/data/designs/ios-alltrails-expo.md +400 -0
- package/dist/data/designs/ios-alltrails-swiftui.md +343 -0
- package/dist/data/designs/ios-alltrails.md +344 -0
- package/dist/data/designs/ios-amazon-android.md +686 -0
- package/dist/data/designs/ios-amazon-expo.md +433 -0
- package/dist/data/designs/ios-amazon-music-android.md +416 -0
- package/dist/data/designs/ios-amazon-music-expo.md +368 -0
- package/dist/data/designs/ios-amazon-music-swiftui.md +436 -0
- package/dist/data/designs/ios-amazon-music.md +389 -0
- package/dist/data/designs/ios-amazon-swiftui.md +460 -0
- package/dist/data/designs/ios-amazon.md +440 -0
- package/dist/data/designs/ios-apple-fitness-android.md +462 -0
- package/dist/data/designs/ios-apple-fitness-expo.md +373 -0
- package/dist/data/designs/ios-apple-fitness-swiftui.md +373 -0
- package/dist/data/designs/ios-apple-fitness.md +399 -0
- package/dist/data/designs/ios-apple-maps-android.md +646 -0
- package/dist/data/designs/ios-apple-maps-expo.md +557 -0
- package/dist/data/designs/ios-apple-maps-swiftui.md +588 -0
- package/dist/data/designs/ios-apple-maps.md +447 -0
- package/dist/data/designs/ios-apple-music-android.md +737 -0
- package/dist/data/designs/ios-apple-music-expo.md +596 -0
- package/dist/data/designs/ios-apple-music-swiftui.md +578 -0
- package/dist/data/designs/ios-apple-music.md +494 -0
- package/dist/data/designs/ios-apple-notes-android.md +675 -0
- package/dist/data/designs/ios-apple-notes-expo.md +468 -0
- package/dist/data/designs/ios-apple-notes-swiftui.md +489 -0
- package/dist/data/designs/ios-apple-notes.md +413 -0
- package/dist/data/designs/ios-apple-tv-android.md +405 -0
- package/dist/data/designs/ios-apple-tv-expo.md +341 -0
- package/dist/data/designs/ios-apple-tv-swiftui.md +348 -0
- package/dist/data/designs/ios-apple-tv.md +390 -0
- package/dist/data/designs/ios-apple-wallet-android.md +617 -0
- package/dist/data/designs/ios-apple-wallet-expo.md +487 -0
- package/dist/data/designs/ios-apple-wallet-swiftui.md +425 -0
- package/dist/data/designs/ios-apple-wallet.md +360 -0
- package/dist/data/designs/ios-asana-android.md +435 -0
- package/dist/data/designs/ios-asana-expo.md +411 -0
- package/dist/data/designs/ios-asana-swiftui.md +416 -0
- package/dist/data/designs/ios-asana.md +433 -0
- package/dist/data/designs/ios-audible-android.md +450 -0
- package/dist/data/designs/ios-audible-expo.md +328 -0
- package/dist/data/designs/ios-audible-swiftui.md +418 -0
- package/dist/data/designs/ios-audible.md +364 -0
- package/dist/data/designs/ios-bandcamp-android.md +471 -0
- package/dist/data/designs/ios-bandcamp-expo.md +378 -0
- package/dist/data/designs/ios-bandcamp-swiftui.md +374 -0
- package/dist/data/designs/ios-bandcamp.md +372 -0
- package/dist/data/designs/ios-bear-android.md +496 -0
- package/dist/data/designs/ios-bear-expo.md +449 -0
- package/dist/data/designs/ios-bear-swiftui.md +479 -0
- package/dist/data/designs/ios-bear.md +412 -0
- package/dist/data/designs/ios-bereal-android.md +392 -0
- package/dist/data/designs/ios-bereal-expo.md +299 -0
- package/dist/data/designs/ios-bereal-swiftui.md +319 -0
- package/dist/data/designs/ios-bereal.md +336 -0
- package/dist/data/designs/ios-binance-android.md +481 -0
- package/dist/data/designs/ios-binance-expo.md +404 -0
- package/dist/data/designs/ios-binance-swiftui.md +418 -0
- package/dist/data/designs/ios-binance.md +410 -0
- package/dist/data/designs/ios-bluesky-android.md +442 -0
- package/dist/data/designs/ios-bluesky-expo.md +362 -0
- package/dist/data/designs/ios-bluesky-swiftui.md +328 -0
- package/dist/data/designs/ios-bluesky.md +338 -0
- package/dist/data/designs/ios-booking-android.md +471 -0
- package/dist/data/designs/ios-booking-expo.md +364 -0
- package/dist/data/designs/ios-booking-swiftui.md +377 -0
- package/dist/data/designs/ios-booking.md +346 -0
- package/dist/data/designs/ios-bumble-android.md +756 -0
- package/dist/data/designs/ios-bumble-expo.md +629 -0
- package/dist/data/designs/ios-bumble-swiftui.md +546 -0
- package/dist/data/designs/ios-bumble.md +424 -0
- package/dist/data/designs/ios-cal-ai-android.md +664 -0
- package/dist/data/designs/ios-cal-ai-expo.md +415 -0
- package/dist/data/designs/ios-cal-ai-swiftui.md +486 -0
- package/dist/data/designs/ios-cal-ai.md +387 -0
- package/dist/data/designs/ios-calm-android.md +431 -0
- package/dist/data/designs/ios-calm-expo.md +384 -0
- package/dist/data/designs/ios-calm-swiftui.md +327 -0
- package/dist/data/designs/ios-calm.md +356 -0
- package/dist/data/designs/ios-canva-android.md +469 -0
- package/dist/data/designs/ios-canva-expo.md +374 -0
- package/dist/data/designs/ios-canva-swiftui.md +422 -0
- package/dist/data/designs/ios-canva.md +395 -0
- package/dist/data/designs/ios-cash-app-android.md +601 -0
- package/dist/data/designs/ios-cash-app-expo.md +453 -0
- package/dist/data/designs/ios-cash-app-swiftui.md +463 -0
- package/dist/data/designs/ios-cash-app.md +365 -0
- package/dist/data/designs/ios-character-ai-android.md +489 -0
- package/dist/data/designs/ios-character-ai-expo.md +420 -0
- package/dist/data/designs/ios-character-ai-swiftui.md +417 -0
- package/dist/data/designs/ios-character-ai.md +392 -0
- package/dist/data/designs/ios-charles-schwab-android.md +454 -0
- package/dist/data/designs/ios-charles-schwab-expo.md +388 -0
- package/dist/data/designs/ios-charles-schwab-swiftui.md +394 -0
- package/dist/data/designs/ios-charles-schwab.md +380 -0
- package/dist/data/designs/ios-chatgpt-android.md +753 -0
- package/dist/data/designs/ios-chatgpt-expo.md +644 -0
- package/dist/data/designs/ios-chatgpt-swiftui.md +653 -0
- package/dist/data/designs/ios-chatgpt.md +466 -0
- package/dist/data/designs/ios-chime-android.md +438 -0
- package/dist/data/designs/ios-chime-expo.md +419 -0
- package/dist/data/designs/ios-chime-swiftui.md +383 -0
- package/dist/data/designs/ios-chime.md +389 -0
- package/dist/data/designs/ios-chipotle-android.md +397 -0
- package/dist/data/designs/ios-chipotle-expo.md +301 -0
- package/dist/data/designs/ios-chipotle-swiftui.md +345 -0
- package/dist/data/designs/ios-chipotle.md +340 -0
- package/dist/data/designs/ios-citymapper-android.md +466 -0
- package/dist/data/designs/ios-citymapper-expo.md +420 -0
- package/dist/data/designs/ios-citymapper-swiftui.md +424 -0
- package/dist/data/designs/ios-citymapper.md +394 -0
- package/dist/data/designs/ios-claude-android.md +625 -0
- package/dist/data/designs/ios-claude-expo.md +671 -0
- package/dist/data/designs/ios-claude-swiftui.md +649 -0
- package/dist/data/designs/ios-claude.md +447 -0
- package/dist/data/designs/ios-clickup-android.md +484 -0
- package/dist/data/designs/ios-clickup-expo.md +423 -0
- package/dist/data/designs/ios-clickup-swiftui.md +416 -0
- package/dist/data/designs/ios-clickup.md +431 -0
- package/dist/data/designs/ios-clubhouse-android.md +524 -0
- package/dist/data/designs/ios-clubhouse-expo.md +425 -0
- package/dist/data/designs/ios-clubhouse-swiftui.md +447 -0
- package/dist/data/designs/ios-clubhouse.md +406 -0
- package/dist/data/designs/ios-coffee-meets-bagel-android.md +461 -0
- package/dist/data/designs/ios-coffee-meets-bagel-expo.md +372 -0
- package/dist/data/designs/ios-coffee-meets-bagel-swiftui.md +368 -0
- package/dist/data/designs/ios-coffee-meets-bagel.md +376 -0
- package/dist/data/designs/ios-coinbase-android.md +611 -0
- package/dist/data/designs/ios-coinbase-expo.md +500 -0
- package/dist/data/designs/ios-coinbase-swiftui.md +490 -0
- package/dist/data/designs/ios-coinbase.md +409 -0
- package/dist/data/designs/ios-copilot-android.md +625 -0
- package/dist/data/designs/ios-copilot-expo.md +423 -0
- package/dist/data/designs/ios-copilot-swiftui.md +471 -0
- package/dist/data/designs/ios-copilot.md +367 -0
- package/dist/data/designs/ios-craft-android.md +459 -0
- package/dist/data/designs/ios-craft-expo.md +417 -0
- package/dist/data/designs/ios-craft-swiftui.md +449 -0
- package/dist/data/designs/ios-craft.md +449 -0
- package/dist/data/designs/ios-crunchyroll-android.md +432 -0
- package/dist/data/designs/ios-crunchyroll-expo.md +388 -0
- package/dist/data/designs/ios-crunchyroll-swiftui.md +417 -0
- package/dist/data/designs/ios-crunchyroll.md +388 -0
- package/dist/data/designs/ios-crypto-com-android.md +461 -0
- package/dist/data/designs/ios-crypto-com-expo.md +404 -0
- package/dist/data/designs/ios-crypto-com-swiftui.md +419 -0
- package/dist/data/designs/ios-crypto-com.md +410 -0
- package/dist/data/designs/ios-deepseek-android.md +471 -0
- package/dist/data/designs/ios-deepseek-expo.md +391 -0
- package/dist/data/designs/ios-deepseek-swiftui.md +354 -0
- package/dist/data/designs/ios-deepseek.md +393 -0
- package/dist/data/designs/ios-deezer-android.md +475 -0
- package/dist/data/designs/ios-deezer-expo.md +400 -0
- package/dist/data/designs/ios-deezer-swiftui.md +430 -0
- package/dist/data/designs/ios-deezer.md +388 -0
- package/dist/data/designs/ios-deliveroo-android.md +493 -0
- package/dist/data/designs/ios-deliveroo-expo.md +424 -0
- package/dist/data/designs/ios-deliveroo-swiftui.md +398 -0
- package/dist/data/designs/ios-deliveroo.md +420 -0
- package/dist/data/designs/ios-delta-android.md +439 -0
- package/dist/data/designs/ios-delta-expo.md +381 -0
- package/dist/data/designs/ios-delta-swiftui.md +400 -0
- package/dist/data/designs/ios-delta.md +397 -0
- package/dist/data/designs/ios-discord-android.md +611 -0
- package/dist/data/designs/ios-discord-expo.md +618 -0
- package/dist/data/designs/ios-discord-swiftui.md +630 -0
- package/dist/data/designs/ios-discord.md +451 -0
- package/dist/data/designs/ios-disney-plus-android.md +464 -0
- package/dist/data/designs/ios-disney-plus-expo.md +358 -0
- package/dist/data/designs/ios-disney-plus-swiftui.md +412 -0
- package/dist/data/designs/ios-disney-plus.md +355 -0
- package/dist/data/designs/ios-dominos-android.md +417 -0
- package/dist/data/designs/ios-dominos-expo.md +316 -0
- package/dist/data/designs/ios-dominos-swiftui.md +340 -0
- package/dist/data/designs/ios-dominos.md +347 -0
- package/dist/data/designs/ios-doordash-android.md +527 -0
- package/dist/data/designs/ios-doordash-expo.md +323 -0
- package/dist/data/designs/ios-doordash-swiftui.md +301 -0
- package/dist/data/designs/ios-doordash.md +393 -0
- package/dist/data/designs/ios-dropbox-android.md +522 -0
- package/dist/data/designs/ios-dropbox-expo.md +393 -0
- package/dist/data/designs/ios-dropbox-swiftui.md +400 -0
- package/dist/data/designs/ios-dropbox.md +374 -0
- package/dist/data/designs/ios-duolingo-android.md +641 -0
- package/dist/data/designs/ios-duolingo-expo.md +597 -0
- package/dist/data/designs/ios-duolingo-swiftui.md +539 -0
- package/dist/data/designs/ios-duolingo.md +409 -0
- package/dist/data/designs/ios-ebay-android.md +436 -0
- package/dist/data/designs/ios-ebay-expo.md +344 -0
- package/dist/data/designs/ios-ebay-swiftui.md +308 -0
- package/dist/data/designs/ios-ebay.md +355 -0
- package/dist/data/designs/ios-espn-android.md +468 -0
- package/dist/data/designs/ios-espn-expo.md +387 -0
- package/dist/data/designs/ios-espn-swiftui.md +410 -0
- package/dist/data/designs/ios-espn.md +384 -0
- package/dist/data/designs/ios-etsy-android.md +439 -0
- package/dist/data/designs/ios-etsy-expo.md +351 -0
- package/dist/data/designs/ios-etsy-swiftui.md +296 -0
- package/dist/data/designs/ios-etsy.md +355 -0
- package/dist/data/designs/ios-evernote-android.md +419 -0
- package/dist/data/designs/ios-evernote-expo.md +379 -0
- package/dist/data/designs/ios-evernote-swiftui.md +380 -0
- package/dist/data/designs/ios-evernote.md +416 -0
- package/dist/data/designs/ios-expedia-android.md +451 -0
- package/dist/data/designs/ios-expedia-expo.md +433 -0
- package/dist/data/designs/ios-expedia-swiftui.md +399 -0
- package/dist/data/designs/ios-expedia.md +431 -0
- package/dist/data/designs/ios-facebook-android.md +628 -0
- package/dist/data/designs/ios-facebook-expo.md +471 -0
- package/dist/data/designs/ios-facebook-swiftui.md +459 -0
- package/dist/data/designs/ios-facebook.md +452 -0
- package/dist/data/designs/ios-feeld-android.md +431 -0
- package/dist/data/designs/ios-feeld-expo.md +379 -0
- package/dist/data/designs/ios-feeld-swiftui.md +401 -0
- package/dist/data/designs/ios-feeld.md +378 -0
- package/dist/data/designs/ios-fidelity-android.md +426 -0
- package/dist/data/designs/ios-fidelity-expo.md +396 -0
- package/dist/data/designs/ios-fidelity-swiftui.md +398 -0
- package/dist/data/designs/ios-fidelity.md +387 -0
- package/dist/data/designs/ios-figma-android.md +578 -0
- package/dist/data/designs/ios-figma-expo.md +504 -0
- package/dist/data/designs/ios-figma-swiftui.md +542 -0
- package/dist/data/designs/ios-figma.md +404 -0
- package/dist/data/designs/ios-fitbit-android.md +439 -0
- package/dist/data/designs/ios-fitbit-expo.md +389 -0
- package/dist/data/designs/ios-fitbit-swiftui.md +380 -0
- package/dist/data/designs/ios-fitbit.md +396 -0
- package/dist/data/designs/ios-flickr-android.md +506 -0
- package/dist/data/designs/ios-flickr-expo.md +434 -0
- package/dist/data/designs/ios-flickr-swiftui.md +421 -0
- package/dist/data/designs/ios-flickr.md +394 -0
- package/dist/data/designs/ios-flighty-android.md +418 -0
- package/dist/data/designs/ios-flighty-expo.md +353 -0
- package/dist/data/designs/ios-flighty-swiftui.md +337 -0
- package/dist/data/designs/ios-flighty.md +357 -0
- package/dist/data/designs/ios-flo-android.md +447 -0
- package/dist/data/designs/ios-flo-expo.md +363 -0
- package/dist/data/designs/ios-flo-swiftui.md +355 -0
- package/dist/data/designs/ios-flo.md +351 -0
- package/dist/data/designs/ios-garmin-connect-android.md +531 -0
- package/dist/data/designs/ios-garmin-connect-expo.md +429 -0
- package/dist/data/designs/ios-garmin-connect-swiftui.md +459 -0
- package/dist/data/designs/ios-garmin-connect.md +419 -0
- package/dist/data/designs/ios-gas-android.md +438 -0
- package/dist/data/designs/ios-gas-expo.md +350 -0
- package/dist/data/designs/ios-gas-swiftui.md +329 -0
- package/dist/data/designs/ios-gas.md +387 -0
- package/dist/data/designs/ios-gemini-android.md +568 -0
- package/dist/data/designs/ios-gemini-expo.md +416 -0
- package/dist/data/designs/ios-gemini-swiftui.md +424 -0
- package/dist/data/designs/ios-gemini.md +351 -0
- package/dist/data/designs/ios-github-android.md +507 -0
- package/dist/data/designs/ios-github-expo.md +486 -0
- package/dist/data/designs/ios-github-swiftui.md +459 -0
- package/dist/data/designs/ios-github.md +394 -0
- package/dist/data/designs/ios-gmail-android.md +548 -0
- package/dist/data/designs/ios-gmail-expo.md +457 -0
- package/dist/data/designs/ios-gmail-swiftui.md +470 -0
- package/dist/data/designs/ios-gmail.md +441 -0
- package/dist/data/designs/ios-goodreads-android.md +436 -0
- package/dist/data/designs/ios-goodreads-expo.md +407 -0
- package/dist/data/designs/ios-goodreads-swiftui.md +378 -0
- package/dist/data/designs/ios-goodreads.md +385 -0
- package/dist/data/designs/ios-google-calendar-android.md +565 -0
- package/dist/data/designs/ios-google-calendar-expo.md +616 -0
- package/dist/data/designs/ios-google-calendar-swiftui.md +585 -0
- package/dist/data/designs/ios-google-calendar.md +431 -0
- package/dist/data/designs/ios-google-maps-android.md +614 -0
- package/dist/data/designs/ios-google-maps-expo.md +515 -0
- package/dist/data/designs/ios-google-maps-swiftui.md +484 -0
- package/dist/data/designs/ios-google-maps.md +451 -0
- package/dist/data/designs/ios-grammarly-android.md +449 -0
- package/dist/data/designs/ios-grammarly-expo.md +383 -0
- package/dist/data/designs/ios-grammarly-swiftui.md +381 -0
- package/dist/data/designs/ios-grammarly.md +392 -0
- package/dist/data/designs/ios-grindr-android.md +494 -0
- package/dist/data/designs/ios-grindr-expo.md +358 -0
- package/dist/data/designs/ios-grindr-swiftui.md +357 -0
- package/dist/data/designs/ios-grindr.md +356 -0
- package/dist/data/designs/ios-grok-android.md +471 -0
- package/dist/data/designs/ios-grok-expo.md +388 -0
- package/dist/data/designs/ios-grok-swiftui.md +397 -0
- package/dist/data/designs/ios-grok.md +357 -0
- package/dist/data/designs/ios-groupme-android.md +551 -0
- package/dist/data/designs/ios-groupme-expo.md +442 -0
- package/dist/data/designs/ios-groupme-swiftui.md +439 -0
- package/dist/data/designs/ios-groupme.md +424 -0
- package/dist/data/designs/ios-grubhub-android.md +506 -0
- package/dist/data/designs/ios-grubhub-expo.md +428 -0
- package/dist/data/designs/ios-grubhub-swiftui.md +408 -0
- package/dist/data/designs/ios-grubhub.md +427 -0
- package/dist/data/designs/ios-happn-android.md +464 -0
- package/dist/data/designs/ios-happn-expo.md +411 -0
- package/dist/data/designs/ios-happn-swiftui.md +401 -0
- package/dist/data/designs/ios-happn.md +400 -0
- package/dist/data/designs/ios-headspace-android.md +688 -0
- package/dist/data/designs/ios-headspace-expo.md +574 -0
- package/dist/data/designs/ios-headspace-swiftui.md +498 -0
- package/dist/data/designs/ios-headspace.md +430 -0
- package/dist/data/designs/ios-hevy-android.md +473 -0
- package/dist/data/designs/ios-hevy-expo.md +405 -0
- package/dist/data/designs/ios-hevy-swiftui.md +367 -0
- package/dist/data/designs/ios-hevy.md +410 -0
- package/dist/data/designs/ios-hinge-android.md +743 -0
- package/dist/data/designs/ios-hinge-expo.md +580 -0
- package/dist/data/designs/ios-hinge-swiftui.md +512 -0
- package/dist/data/designs/ios-hinge.md +405 -0
- package/dist/data/designs/ios-hopper-android.md +440 -0
- package/dist/data/designs/ios-hopper-expo.md +353 -0
- package/dist/data/designs/ios-hopper-swiftui.md +340 -0
- package/dist/data/designs/ios-hopper.md +354 -0
- package/dist/data/designs/ios-hulu-android.md +435 -0
- package/dist/data/designs/ios-hulu-expo.md +331 -0
- package/dist/data/designs/ios-hulu-swiftui.md +374 -0
- package/dist/data/designs/ios-hulu.md +353 -0
- package/dist/data/designs/ios-iheartradio-android.md +481 -0
- package/dist/data/designs/ios-iheartradio-expo.md +417 -0
- package/dist/data/designs/ios-iheartradio-swiftui.md +423 -0
- package/dist/data/designs/ios-iheartradio.md +381 -0
- package/dist/data/designs/ios-imessage-android.md +426 -0
- package/dist/data/designs/ios-imessage-expo.md +384 -0
- package/dist/data/designs/ios-imessage-swiftui.md +378 -0
- package/dist/data/designs/ios-imessage.md +394 -0
- package/dist/data/designs/ios-instacart-android.md +471 -0
- package/dist/data/designs/ios-instacart-expo.md +349 -0
- package/dist/data/designs/ios-instacart-swiftui.md +378 -0
- package/dist/data/designs/ios-instacart.md +360 -0
- package/dist/data/designs/ios-instagram-android.md +646 -0
- package/dist/data/designs/ios-instagram-expo.md +310 -0
- package/dist/data/designs/ios-instagram-swiftui.md +333 -0
- package/dist/data/designs/ios-instagram.md +376 -0
- package/dist/data/designs/ios-jira-android.md +432 -0
- package/dist/data/designs/ios-jira-expo.md +375 -0
- package/dist/data/designs/ios-jira-swiftui.md +433 -0
- package/dist/data/designs/ios-jira.md +424 -0
- package/dist/data/designs/ios-kakaotalk-android.md +491 -0
- package/dist/data/designs/ios-kakaotalk-expo.md +418 -0
- package/dist/data/designs/ios-kakaotalk-swiftui.md +406 -0
- package/dist/data/designs/ios-kakaotalk.md +414 -0
- package/dist/data/designs/ios-kayak-android.md +466 -0
- package/dist/data/designs/ios-kayak-expo.md +407 -0
- package/dist/data/designs/ios-kayak-swiftui.md +426 -0
- package/dist/data/designs/ios-kayak.md +392 -0
- package/dist/data/designs/ios-kick-android.md +487 -0
- package/dist/data/designs/ios-kick-expo.md +434 -0
- package/dist/data/designs/ios-kick-swiftui.md +429 -0
- package/dist/data/designs/ios-kick.md +389 -0
- package/dist/data/designs/ios-kik-android.md +433 -0
- package/dist/data/designs/ios-kik-expo.md +363 -0
- package/dist/data/designs/ios-kik-swiftui.md +363 -0
- package/dist/data/designs/ios-kik.md +392 -0
- package/dist/data/designs/ios-kindle-android.md +439 -0
- package/dist/data/designs/ios-kindle-expo.md +356 -0
- package/dist/data/designs/ios-kindle-swiftui.md +372 -0
- package/dist/data/designs/ios-kindle.md +369 -0
- package/dist/data/designs/ios-klarna-android.md +420 -0
- package/dist/data/designs/ios-klarna-expo.md +349 -0
- package/dist/data/designs/ios-klarna-swiftui.md +360 -0
- package/dist/data/designs/ios-klarna.md +356 -0
- package/dist/data/designs/ios-lemon8-android.md +492 -0
- package/dist/data/designs/ios-lemon8-expo.md +413 -0
- package/dist/data/designs/ios-lemon8-swiftui.md +428 -0
- package/dist/data/designs/ios-lemon8.md +378 -0
- package/dist/data/designs/ios-life360-android.md +474 -0
- package/dist/data/designs/ios-life360-expo.md +375 -0
- package/dist/data/designs/ios-life360-swiftui.md +390 -0
- package/dist/data/designs/ios-life360.md +402 -0
- package/dist/data/designs/ios-line-android.md +517 -0
- package/dist/data/designs/ios-line-expo.md +402 -0
- package/dist/data/designs/ios-line-swiftui.md +361 -0
- package/dist/data/designs/ios-line.md +362 -0
- package/dist/data/designs/ios-linear-android.md +457 -0
- package/dist/data/designs/ios-linear-expo.md +367 -0
- package/dist/data/designs/ios-linear-swiftui.md +401 -0
- package/dist/data/designs/ios-linear.md +368 -0
- package/dist/data/designs/ios-linkedin-android.md +620 -0
- package/dist/data/designs/ios-linkedin-expo.md +469 -0
- package/dist/data/designs/ios-linkedin-swiftui.md +472 -0
- package/dist/data/designs/ios-linkedin.md +444 -0
- package/dist/data/designs/ios-locket-android.md +500 -0
- package/dist/data/designs/ios-locket-expo.md +375 -0
- package/dist/data/designs/ios-locket-swiftui.md +375 -0
- package/dist/data/designs/ios-locket.md +381 -0
- package/dist/data/designs/ios-lyft-android.md +406 -0
- package/dist/data/designs/ios-lyft-expo.md +333 -0
- package/dist/data/designs/ios-lyft-swiftui.md +315 -0
- package/dist/data/designs/ios-lyft.md +337 -0
- package/dist/data/designs/ios-marriott-bonvoy-android.md +488 -0
- package/dist/data/designs/ios-marriott-bonvoy-expo.md +412 -0
- package/dist/data/designs/ios-marriott-bonvoy-swiftui.md +461 -0
- package/dist/data/designs/ios-marriott-bonvoy.md +379 -0
- package/dist/data/designs/ios-mastodon-android.md +476 -0
- package/dist/data/designs/ios-mastodon-expo.md +379 -0
- package/dist/data/designs/ios-mastodon-swiftui.md +349 -0
- package/dist/data/designs/ios-mastodon.md +337 -0
- package/dist/data/designs/ios-match-android.md +496 -0
- package/dist/data/designs/ios-match-expo.md +393 -0
- package/dist/data/designs/ios-match-swiftui.md +450 -0
- package/dist/data/designs/ios-match.md +382 -0
- package/dist/data/designs/ios-max-android.md +454 -0
- package/dist/data/designs/ios-max-expo.md +342 -0
- package/dist/data/designs/ios-max-swiftui.md +332 -0
- package/dist/data/designs/ios-max.md +352 -0
- package/dist/data/designs/ios-mcdonalds-android.md +497 -0
- package/dist/data/designs/ios-mcdonalds-expo.md +382 -0
- package/dist/data/designs/ios-mcdonalds-swiftui.md +394 -0
- package/dist/data/designs/ios-mcdonalds.md +410 -0
- package/dist/data/designs/ios-messenger-android.md +523 -0
- package/dist/data/designs/ios-messenger-expo.md +379 -0
- package/dist/data/designs/ios-messenger-swiftui.md +369 -0
- package/dist/data/designs/ios-messenger.md +361 -0
- package/dist/data/designs/ios-microsoft-teams-android.md +446 -0
- package/dist/data/designs/ios-microsoft-teams-expo.md +362 -0
- package/dist/data/designs/ios-microsoft-teams-swiftui.md +384 -0
- package/dist/data/designs/ios-microsoft-teams.md +359 -0
- package/dist/data/designs/ios-midjourney-android.md +503 -0
- package/dist/data/designs/ios-midjourney-expo.md +406 -0
- package/dist/data/designs/ios-midjourney-swiftui.md +376 -0
- package/dist/data/designs/ios-midjourney.md +407 -0
- package/dist/data/designs/ios-miro-android.md +475 -0
- package/dist/data/designs/ios-miro-expo.md +421 -0
- package/dist/data/designs/ios-miro-swiftui.md +407 -0
- package/dist/data/designs/ios-miro.md +424 -0
- package/dist/data/designs/ios-monday-android.md +426 -0
- package/dist/data/designs/ios-monday-expo.md +382 -0
- package/dist/data/designs/ios-monday-swiftui.md +386 -0
- package/dist/data/designs/ios-monday.md +415 -0
- package/dist/data/designs/ios-monzo-android.md +423 -0
- package/dist/data/designs/ios-monzo-expo.md +391 -0
- package/dist/data/designs/ios-monzo-swiftui.md +371 -0
- package/dist/data/designs/ios-monzo.md +382 -0
- package/dist/data/designs/ios-myfitnesspal-android.md +631 -0
- package/dist/data/designs/ios-myfitnesspal-expo.md +489 -0
- package/dist/data/designs/ios-myfitnesspal-swiftui.md +500 -0
- package/dist/data/designs/ios-myfitnesspal.md +408 -0
- package/dist/data/designs/ios-netflix-android.md +570 -0
- package/dist/data/designs/ios-netflix-expo.md +528 -0
- package/dist/data/designs/ios-netflix-swiftui.md +543 -0
- package/dist/data/designs/ios-netflix.md +476 -0
- package/dist/data/designs/ios-nextdoor-android.md +399 -0
- package/dist/data/designs/ios-nextdoor-expo.md +317 -0
- package/dist/data/designs/ios-nextdoor-swiftui.md +343 -0
- package/dist/data/designs/ios-nextdoor.md +332 -0
- package/dist/data/designs/ios-nike-run-club-android.md +598 -0
- package/dist/data/designs/ios-nike-run-club-expo.md +459 -0
- package/dist/data/designs/ios-nike-run-club-swiftui.md +474 -0
- package/dist/data/designs/ios-nike-run-club.md +400 -0
- package/dist/data/designs/ios-noom-android.md +455 -0
- package/dist/data/designs/ios-noom-expo.md +362 -0
- package/dist/data/designs/ios-noom-swiftui.md +369 -0
- package/dist/data/designs/ios-noom.md +404 -0
- package/dist/data/designs/ios-notion-android.md +575 -0
- package/dist/data/designs/ios-notion-expo.md +543 -0
- package/dist/data/designs/ios-notion-swiftui.md +562 -0
- package/dist/data/designs/ios-notion.md +506 -0
- package/dist/data/designs/ios-nubank-android.md +447 -0
- package/dist/data/designs/ios-nubank-expo.md +391 -0
- package/dist/data/designs/ios-nubank-swiftui.md +391 -0
- package/dist/data/designs/ios-nubank.md +388 -0
- package/dist/data/designs/ios-obsidian-android.md +456 -0
- package/dist/data/designs/ios-obsidian-expo.md +401 -0
- package/dist/data/designs/ios-obsidian-swiftui.md +412 -0
- package/dist/data/designs/ios-obsidian.md +363 -0
- package/dist/data/designs/ios-okcupid-android.md +496 -0
- package/dist/data/designs/ios-okcupid-expo.md +378 -0
- package/dist/data/designs/ios-okcupid-swiftui.md +343 -0
- package/dist/data/designs/ios-okcupid.md +364 -0
- package/dist/data/designs/ios-opentable-android.md +426 -0
- package/dist/data/designs/ios-opentable-expo.md +350 -0
- package/dist/data/designs/ios-opentable-swiftui.md +328 -0
- package/dist/data/designs/ios-opentable.md +411 -0
- package/dist/data/designs/ios-oura-android.md +426 -0
- package/dist/data/designs/ios-oura-expo.md +353 -0
- package/dist/data/designs/ios-oura-swiftui.md +327 -0
- package/dist/data/designs/ios-oura.md +356 -0
- package/dist/data/designs/ios-overcast-android.md +508 -0
- package/dist/data/designs/ios-overcast-expo.md +420 -0
- package/dist/data/designs/ios-overcast-swiftui.md +427 -0
- package/dist/data/designs/ios-overcast.md +414 -0
- package/dist/data/designs/ios-pandora-android.md +477 -0
- package/dist/data/designs/ios-pandora-expo.md +406 -0
- package/dist/data/designs/ios-pandora-swiftui.md +413 -0
- package/dist/data/designs/ios-pandora.md +366 -0
- package/dist/data/designs/ios-panera-android.md +414 -0
- package/dist/data/designs/ios-panera-expo.md +352 -0
- package/dist/data/designs/ios-panera-swiftui.md +369 -0
- package/dist/data/designs/ios-panera.md +412 -0
- package/dist/data/designs/ios-paramount-plus-android.md +415 -0
- package/dist/data/designs/ios-paramount-plus-expo.md +363 -0
- package/dist/data/designs/ios-paramount-plus-swiftui.md +372 -0
- package/dist/data/designs/ios-paramount-plus.md +400 -0
- package/dist/data/designs/ios-paypal-android.md +535 -0
- package/dist/data/designs/ios-paypal-expo.md +429 -0
- package/dist/data/designs/ios-paypal-swiftui.md +466 -0
- package/dist/data/designs/ios-paypal.md +393 -0
- package/dist/data/designs/ios-peacock-android.md +453 -0
- package/dist/data/designs/ios-peacock-expo.md +393 -0
- package/dist/data/designs/ios-peacock-swiftui.md +420 -0
- package/dist/data/designs/ios-peacock.md +411 -0
- package/dist/data/designs/ios-peloton-android.md +470 -0
- package/dist/data/designs/ios-peloton-expo.md +401 -0
- package/dist/data/designs/ios-peloton-swiftui.md +409 -0
- package/dist/data/designs/ios-peloton.md +415 -0
- package/dist/data/designs/ios-perplexity-android.md +553 -0
- package/dist/data/designs/ios-perplexity-expo.md +703 -0
- package/dist/data/designs/ios-perplexity-swiftui.md +602 -0
- package/dist/data/designs/ios-perplexity.md +453 -0
- package/dist/data/designs/ios-pinterest-android.md +551 -0
- package/dist/data/designs/ios-pinterest-expo.md +466 -0
- package/dist/data/designs/ios-pinterest-swiftui.md +464 -0
- package/dist/data/designs/ios-pinterest.md +427 -0
- package/dist/data/designs/ios-plenty-of-fish-android.md +462 -0
- package/dist/data/designs/ios-plenty-of-fish-expo.md +380 -0
- package/dist/data/designs/ios-plenty-of-fish-swiftui.md +429 -0
- package/dist/data/designs/ios-plenty-of-fish.md +387 -0
- package/dist/data/designs/ios-plex-android.md +403 -0
- package/dist/data/designs/ios-plex-expo.md +333 -0
- package/dist/data/designs/ios-plex-swiftui.md +349 -0
- package/dist/data/designs/ios-plex.md +379 -0
- package/dist/data/designs/ios-pluto-tv-android.md +413 -0
- package/dist/data/designs/ios-pluto-tv-expo.md +379 -0
- package/dist/data/designs/ios-pluto-tv-swiftui.md +411 -0
- package/dist/data/designs/ios-pluto-tv.md +362 -0
- package/dist/data/designs/ios-pocket-casts-android.md +492 -0
- package/dist/data/designs/ios-pocket-casts-expo.md +430 -0
- package/dist/data/designs/ios-pocket-casts-swiftui.md +479 -0
- package/dist/data/designs/ios-pocket-casts.md +427 -0
- package/dist/data/designs/ios-postman-android.md +472 -0
- package/dist/data/designs/ios-postman-expo.md +372 -0
- package/dist/data/designs/ios-postman-swiftui.md +377 -0
- package/dist/data/designs/ios-postman.md +405 -0
- package/dist/data/designs/ios-prime-video-android.md +447 -0
- package/dist/data/designs/ios-prime-video-expo.md +365 -0
- package/dist/data/designs/ios-prime-video-swiftui.md +333 -0
- package/dist/data/designs/ios-prime-video.md +351 -0
- package/dist/data/designs/ios-quora-android.md +513 -0
- package/dist/data/designs/ios-quora-expo.md +362 -0
- package/dist/data/designs/ios-quora-swiftui.md +375 -0
- package/dist/data/designs/ios-quora.md +359 -0
- package/dist/data/designs/ios-raya-android.md +401 -0
- package/dist/data/designs/ios-raya-expo.md +366 -0
- package/dist/data/designs/ios-raya-swiftui.md +393 -0
- package/dist/data/designs/ios-raya.md +366 -0
- package/dist/data/designs/ios-reddit-android.md +605 -0
- package/dist/data/designs/ios-reddit-expo.md +553 -0
- package/dist/data/designs/ios-reddit-swiftui.md +514 -0
- package/dist/data/designs/ios-reddit.md +460 -0
- package/dist/data/designs/ios-resy-android.md +430 -0
- package/dist/data/designs/ios-resy-expo.md +344 -0
- package/dist/data/designs/ios-resy-swiftui.md +330 -0
- package/dist/data/designs/ios-resy.md +412 -0
- package/dist/data/designs/ios-revolut-android.md +433 -0
- package/dist/data/designs/ios-revolut-expo.md +379 -0
- package/dist/data/designs/ios-revolut-swiftui.md +372 -0
- package/dist/data/designs/ios-revolut.md +353 -0
- package/dist/data/designs/ios-robinhood-android.md +587 -0
- package/dist/data/designs/ios-robinhood-expo.md +416 -0
- package/dist/data/designs/ios-robinhood-swiftui.md +431 -0
- package/dist/data/designs/ios-robinhood.md +380 -0
- package/dist/data/designs/ios-runna-android.md +485 -0
- package/dist/data/designs/ios-runna-expo.md +388 -0
- package/dist/data/designs/ios-runna-swiftui.md +413 -0
- package/dist/data/designs/ios-runna.md +390 -0
- package/dist/data/designs/ios-shazam-android.md +436 -0
- package/dist/data/designs/ios-shazam-expo.md +348 -0
- package/dist/data/designs/ios-shazam-swiftui.md +394 -0
- package/dist/data/designs/ios-shazam.md +338 -0
- package/dist/data/designs/ios-signal-android.md +462 -0
- package/dist/data/designs/ios-signal-expo.md +361 -0
- package/dist/data/designs/ios-signal-swiftui.md +350 -0
- package/dist/data/designs/ios-signal.md +357 -0
- package/dist/data/designs/ios-skype-android.md +398 -0
- package/dist/data/designs/ios-skype-expo.md +368 -0
- package/dist/data/designs/ios-skype-swiftui.md +369 -0
- package/dist/data/designs/ios-skype.md +406 -0
- package/dist/data/designs/ios-skyscanner-android.md +451 -0
- package/dist/data/designs/ios-skyscanner-expo.md +423 -0
- package/dist/data/designs/ios-skyscanner-swiftui.md +419 -0
- package/dist/data/designs/ios-skyscanner.md +421 -0
- package/dist/data/designs/ios-slack-android.md +695 -0
- package/dist/data/designs/ios-slack-expo.md +601 -0
- package/dist/data/designs/ios-slack-swiftui.md +594 -0
- package/dist/data/designs/ios-slack.md +474 -0
- package/dist/data/designs/ios-sleep-cycle-android.md +436 -0
- package/dist/data/designs/ios-sleep-cycle-expo.md +380 -0
- package/dist/data/designs/ios-sleep-cycle-swiftui.md +409 -0
- package/dist/data/designs/ios-sleep-cycle.md +386 -0
- package/dist/data/designs/ios-snapchat-android.md +671 -0
- package/dist/data/designs/ios-snapchat-expo.md +528 -0
- package/dist/data/designs/ios-snapchat-swiftui.md +557 -0
- package/dist/data/designs/ios-snapchat.md +478 -0
- package/dist/data/designs/ios-sofi-android.md +446 -0
- package/dist/data/designs/ios-sofi-expo.md +387 -0
- package/dist/data/designs/ios-sofi-swiftui.md +379 -0
- package/dist/data/designs/ios-sofi.md +385 -0
- package/dist/data/designs/ios-soundcloud-android.md +488 -0
- package/dist/data/designs/ios-soundcloud-expo.md +364 -0
- package/dist/data/designs/ios-soundcloud-swiftui.md +399 -0
- package/dist/data/designs/ios-soundcloud.md +359 -0
- package/dist/data/designs/ios-southwest-android.md +446 -0
- package/dist/data/designs/ios-southwest-expo.md +389 -0
- package/dist/data/designs/ios-southwest-swiftui.md +405 -0
- package/dist/data/designs/ios-southwest.md +405 -0
- package/dist/data/designs/ios-spotify-android.md +427 -0
- package/dist/data/designs/ios-spotify-expo.md +321 -0
- package/dist/data/designs/ios-spotify-swiftui.md +312 -0
- package/dist/data/designs/ios-spotify.md +366 -0
- package/dist/data/designs/ios-starbucks-android.md +622 -0
- package/dist/data/designs/ios-starbucks-expo.md +497 -0
- package/dist/data/designs/ios-starbucks-swiftui.md +500 -0
- package/dist/data/designs/ios-starbucks.md +440 -0
- package/dist/data/designs/ios-strava-android.md +656 -0
- package/dist/data/designs/ios-strava-expo.md +501 -0
- package/dist/data/designs/ios-strava-swiftui.md +465 -0
- package/dist/data/designs/ios-strava.md +433 -0
- package/dist/data/designs/ios-strong-android.md +479 -0
- package/dist/data/designs/ios-strong-expo.md +396 -0
- package/dist/data/designs/ios-strong-swiftui.md +363 -0
- package/dist/data/designs/ios-strong.md +415 -0
- package/dist/data/designs/ios-substack-android.md +440 -0
- package/dist/data/designs/ios-substack-expo.md +395 -0
- package/dist/data/designs/ios-substack-swiftui.md +371 -0
- package/dist/data/designs/ios-substack.md +372 -0
- package/dist/data/designs/ios-swiggy-android.md +549 -0
- package/dist/data/designs/ios-swiggy-expo.md +443 -0
- package/dist/data/designs/ios-swiggy-swiftui.md +471 -0
- package/dist/data/designs/ios-swiggy.md +432 -0
- package/dist/data/designs/ios-taco-bell-android.md +529 -0
- package/dist/data/designs/ios-taco-bell-expo.md +407 -0
- package/dist/data/designs/ios-taco-bell-swiftui.md +419 -0
- package/dist/data/designs/ios-taco-bell.md +430 -0
- package/dist/data/designs/ios-telegram-android.md +676 -0
- package/dist/data/designs/ios-telegram-expo.md +594 -0
- package/dist/data/designs/ios-telegram-swiftui.md +518 -0
- package/dist/data/designs/ios-telegram.md +458 -0
- package/dist/data/designs/ios-the-league-android.md +454 -0
- package/dist/data/designs/ios-the-league-expo.md +391 -0
- package/dist/data/designs/ios-the-league-swiftui.md +409 -0
- package/dist/data/designs/ios-the-league.md +368 -0
- package/dist/data/designs/ios-things-3-android.md +421 -0
- package/dist/data/designs/ios-things-3-expo.md +351 -0
- package/dist/data/designs/ios-things-3-swiftui.md +359 -0
- package/dist/data/designs/ios-things-3.md +339 -0
- package/dist/data/designs/ios-threads-android.md +588 -0
- package/dist/data/designs/ios-threads-expo.md +468 -0
- package/dist/data/designs/ios-threads-swiftui.md +452 -0
- package/dist/data/designs/ios-threads.md +424 -0
- package/dist/data/designs/ios-threema-android.md +496 -0
- package/dist/data/designs/ios-threema-expo.md +415 -0
- package/dist/data/designs/ios-threema-swiftui.md +446 -0
- package/dist/data/designs/ios-threema.md +397 -0
- package/dist/data/designs/ios-tidal-android.md +398 -0
- package/dist/data/designs/ios-tidal-expo.md +323 -0
- package/dist/data/designs/ios-tidal-swiftui.md +340 -0
- package/dist/data/designs/ios-tidal.md +351 -0
- package/dist/data/designs/ios-tiktok-android.md +654 -0
- package/dist/data/designs/ios-tiktok-expo.md +578 -0
- package/dist/data/designs/ios-tiktok-swiftui.md +595 -0
- package/dist/data/designs/ios-tiktok.md +387 -0
- package/dist/data/designs/ios-tinder-android.md +681 -0
- package/dist/data/designs/ios-tinder-expo.md +512 -0
- package/dist/data/designs/ios-tinder-swiftui.md +497 -0
- package/dist/data/designs/ios-tinder.md +415 -0
- package/dist/data/designs/ios-todoist-android.md +711 -0
- package/dist/data/designs/ios-todoist-expo.md +607 -0
- package/dist/data/designs/ios-todoist-swiftui.md +573 -0
- package/dist/data/designs/ios-todoist.md +418 -0
- package/dist/data/designs/ios-trello-android.md +412 -0
- package/dist/data/designs/ios-trello-expo.md +332 -0
- package/dist/data/designs/ios-trello-swiftui.md +359 -0
- package/dist/data/designs/ios-trello.md +344 -0
- package/dist/data/designs/ios-tripadvisor-android.md +450 -0
- package/dist/data/designs/ios-tripadvisor-expo.md +322 -0
- package/dist/data/designs/ios-tripadvisor-swiftui.md +326 -0
- package/dist/data/designs/ios-tripadvisor.md +356 -0
- package/dist/data/designs/ios-tubi-android.md +453 -0
- package/dist/data/designs/ios-tubi-expo.md +419 -0
- package/dist/data/designs/ios-tubi-swiftui.md +463 -0
- package/dist/data/designs/ios-tubi.md +383 -0
- package/dist/data/designs/ios-tumblr-android.md +462 -0
- package/dist/data/designs/ios-tumblr-expo.md +374 -0
- package/dist/data/designs/ios-tumblr-swiftui.md +380 -0
- package/dist/data/designs/ios-tumblr.md +337 -0
- package/dist/data/designs/ios-turo-android.md +500 -0
- package/dist/data/designs/ios-turo-expo.md +454 -0
- package/dist/data/designs/ios-turo-swiftui.md +447 -0
- package/dist/data/designs/ios-turo.md +392 -0
- package/dist/data/designs/ios-twitch-android.md +412 -0
- package/dist/data/designs/ios-twitch-expo.md +342 -0
- package/dist/data/designs/ios-twitch-swiftui.md +328 -0
- package/dist/data/designs/ios-twitch.md +354 -0
- package/dist/data/designs/ios-uber-android.md +637 -0
- package/dist/data/designs/ios-uber-eats-android.md +502 -0
- package/dist/data/designs/ios-uber-eats-expo.md +407 -0
- package/dist/data/designs/ios-uber-eats-swiftui.md +393 -0
- package/dist/data/designs/ios-uber-eats.md +353 -0
- package/dist/data/designs/ios-uber-expo.md +512 -0
- package/dist/data/designs/ios-uber-swiftui.md +483 -0
- package/dist/data/designs/ios-uber.md +395 -0
- package/dist/data/designs/ios-united-airlines-android.md +494 -0
- package/dist/data/designs/ios-united-airlines-expo.md +408 -0
- package/dist/data/designs/ios-united-airlines-swiftui.md +453 -0
- package/dist/data/designs/ios-united-airlines.md +388 -0
- package/dist/data/designs/ios-venmo-android.md +654 -0
- package/dist/data/designs/ios-venmo-expo.md +458 -0
- package/dist/data/designs/ios-venmo-swiftui.md +469 -0
- package/dist/data/designs/ios-venmo.md +436 -0
- package/dist/data/designs/ios-vercel-android.md +454 -0
- package/dist/data/designs/ios-vercel-expo.md +387 -0
- package/dist/data/designs/ios-vercel-swiftui.md +374 -0
- package/dist/data/designs/ios-vercel.md +399 -0
- package/dist/data/designs/ios-vero-android.md +498 -0
- package/dist/data/designs/ios-vero-expo.md +376 -0
- package/dist/data/designs/ios-vero-swiftui.md +374 -0
- package/dist/data/designs/ios-vero.md +394 -0
- package/dist/data/designs/ios-viber-android.md +377 -0
- package/dist/data/designs/ios-viber-expo.md +375 -0
- package/dist/data/designs/ios-viber-swiftui.md +378 -0
- package/dist/data/designs/ios-viber.md +403 -0
- package/dist/data/designs/ios-vimeo-android.md +465 -0
- package/dist/data/designs/ios-vimeo-expo.md +347 -0
- package/dist/data/designs/ios-vimeo-swiftui.md +383 -0
- package/dist/data/designs/ios-vimeo.md +403 -0
- package/dist/data/designs/ios-vrbo-android.md +488 -0
- package/dist/data/designs/ios-vrbo-expo.md +395 -0
- package/dist/data/designs/ios-vrbo-swiftui.md +421 -0
- package/dist/data/designs/ios-vrbo.md +383 -0
- package/dist/data/designs/ios-vsco-android.md +490 -0
- package/dist/data/designs/ios-vsco-expo.md +413 -0
- package/dist/data/designs/ios-vsco-swiftui.md +449 -0
- package/dist/data/designs/ios-vsco.md +412 -0
- package/dist/data/designs/ios-walmart-android.md +479 -0
- package/dist/data/designs/ios-walmart-expo.md +370 -0
- package/dist/data/designs/ios-walmart-swiftui.md +387 -0
- package/dist/data/designs/ios-walmart.md +366 -0
- package/dist/data/designs/ios-waze-android.md +695 -0
- package/dist/data/designs/ios-waze-expo.md +587 -0
- package/dist/data/designs/ios-waze-swiftui.md +601 -0
- package/dist/data/designs/ios-waze.md +453 -0
- package/dist/data/designs/ios-webull-android.md +429 -0
- package/dist/data/designs/ios-webull-expo.md +384 -0
- package/dist/data/designs/ios-webull-swiftui.md +406 -0
- package/dist/data/designs/ios-webull.md +375 -0
- package/dist/data/designs/ios-wechat-android.md +545 -0
- package/dist/data/designs/ios-wechat-expo.md +398 -0
- package/dist/data/designs/ios-wechat-swiftui.md +394 -0
- package/dist/data/designs/ios-wechat.md +363 -0
- package/dist/data/designs/ios-whatsapp-android.md +568 -0
- package/dist/data/designs/ios-whatsapp-expo.md +502 -0
- package/dist/data/designs/ios-whatsapp-swiftui.md +483 -0
- package/dist/data/designs/ios-whatsapp.md +425 -0
- package/dist/data/designs/ios-whoop-android.md +635 -0
- package/dist/data/designs/ios-whoop-expo.md +562 -0
- package/dist/data/designs/ios-whoop-swiftui.md +553 -0
- package/dist/data/designs/ios-whoop.md +419 -0
- package/dist/data/designs/ios-wise-android.md +428 -0
- package/dist/data/designs/ios-wise-expo.md +357 -0
- package/dist/data/designs/ios-wise-swiftui.md +356 -0
- package/dist/data/designs/ios-wise.md +361 -0
- package/dist/data/designs/ios-x-twitter-android.md +618 -0
- package/dist/data/designs/ios-x-twitter-expo.md +442 -0
- package/dist/data/designs/ios-x-twitter-swiftui.md +429 -0
- package/dist/data/designs/ios-x-twitter.md +432 -0
- package/dist/data/designs/ios-yelp-android.md +455 -0
- package/dist/data/designs/ios-yelp-expo.md +397 -0
- package/dist/data/designs/ios-yelp-swiftui.md +369 -0
- package/dist/data/designs/ios-yelp.md +398 -0
- package/dist/data/designs/ios-youtube-android.md +668 -0
- package/dist/data/designs/ios-youtube-expo.md +435 -0
- package/dist/data/designs/ios-youtube-music-android.md +450 -0
- package/dist/data/designs/ios-youtube-music-expo.md +404 -0
- package/dist/data/designs/ios-youtube-music-swiftui.md +434 -0
- package/dist/data/designs/ios-youtube-music.md +381 -0
- package/dist/data/designs/ios-youtube-swiftui.md +496 -0
- package/dist/data/designs/ios-youtube.md +466 -0
- package/dist/data/designs/ios-zalo-android.md +510 -0
- package/dist/data/designs/ios-zalo-expo.md +389 -0
- package/dist/data/designs/ios-zalo-swiftui.md +431 -0
- package/dist/data/designs/ios-zalo.md +388 -0
- package/dist/data/designs/ios-zomato-android.md +527 -0
- package/dist/data/designs/ios-zomato-expo.md +437 -0
- package/dist/data/designs/ios-zomato-swiftui.md +433 -0
- package/dist/data/designs/ios-zomato.md +439 -0
- package/dist/data/designs/ios-zoom-android.md +423 -0
- package/dist/data/designs/ios-zoom-expo.md +355 -0
- package/dist/data/designs/ios-zoom-swiftui.md +379 -0
- package/dist/data/designs/ios-zoom.md +357 -0
- package/dist/data/designs/ios-zwift-android.md +402 -0
- package/dist/data/designs/ios-zwift-expo.md +402 -0
- package/dist/data/designs/ios-zwift-swiftui.md +378 -0
- package/dist/data/designs/ios-zwift.md +411 -0
- package/dist/data/designs/kraken.md +125 -0
- package/dist/data/designs/lamborghini.md +288 -0
- package/dist/data/designs/linear.app.md +548 -0
- package/dist/data/designs/lovable.md +298 -0
- package/dist/data/designs/mastercard.md +365 -0
- package/dist/data/designs/material-clean.md +289 -0
- package/dist/data/designs/meta.md +683 -0
- package/dist/data/designs/midnight-pro.md +256 -0
- package/dist/data/designs/minimal-brutalist.md +245 -0
- package/dist/data/designs/minimax.md +746 -0
- package/dist/data/designs/mintlify.md +852 -0
- package/dist/data/designs/miro.md +825 -0
- package/dist/data/designs/mistral.ai.md +773 -0
- package/dist/data/designs/mongodb.md +767 -0
- package/dist/data/designs/nike.md +575 -0
- package/dist/data/designs/nintendo-2001.md +649 -0
- package/dist/data/designs/notion.md +821 -0
- package/dist/data/designs/nova-mobile-design-md.md +235 -0
- package/dist/data/designs/nvidia.md +640 -0
- package/dist/data/designs/ollama.md +539 -0
- package/dist/data/designs/opencode.ai.md +521 -0
- package/dist/data/designs/pinterest.md +597 -0
- package/dist/data/designs/playful-color.md +253 -0
- package/dist/data/designs/playstation.md +661 -0
- package/dist/data/designs/posthog.md +690 -0
- package/dist/data/designs/raycast.md +669 -0
- package/dist/data/designs/renault.md +589 -0
- package/dist/data/designs/replicate.md +616 -0
- package/dist/data/designs/resend.md +585 -0
- package/dist/data/designs/revolut.md +636 -0
- package/dist/data/designs/runwayml.md +244 -0
- package/dist/data/designs/sanity.md +357 -0
- package/dist/data/designs/sentry.md +551 -0
- package/dist/data/designs/shopify.md +516 -0
- package/dist/data/designs/slack.md +482 -0
- package/dist/data/designs/soft-glass.md +258 -0
- package/dist/data/designs/spacex.md +363 -0
- package/dist/data/designs/spotify.md +246 -0
- package/dist/data/designs/starbucks.md +580 -0
- package/dist/data/designs/stripe.md +487 -0
- package/dist/data/designs/supabase.md +462 -0
- package/dist/data/designs/superhuman.md +448 -0
- package/dist/data/designs/tesla.md +286 -0
- package/dist/data/designs/theverge.md +339 -0
- package/dist/data/designs/together.ai.md +633 -0
- package/dist/data/designs/uber.md +636 -0
- package/dist/data/designs/vercel.md +736 -0
- package/dist/data/designs/vodafone.md +538 -0
- package/dist/data/designs/voltagent.md +521 -0
- package/dist/data/designs/warp.md +526 -0
- package/dist/data/designs/webflow.md +588 -0
- package/dist/data/designs/wired.md +497 -0
- package/dist/data/designs/wise.md +544 -0
- package/dist/data/designs/x.ai.md +465 -0
- package/dist/data/designs/zapier.md +537 -0
- package/dist/data/index.json +2600 -0
- package/dist/httpServer.js +83 -0
- package/dist/index.js +13 -0
- package/dist/server.js +32 -0
- package/dist/services/designStore.js +59 -0
- package/dist/services/linter.js +87 -0
- package/dist/services/sectionParser.js +154 -0
- package/dist/tools/authoring.js +131 -0
- package/dist/tools/browse.js +232 -0
- package/dist/tools/diff.js +88 -0
- package/dist/tools/getDesign.js +216 -0
- package/dist/tools/guardrails.js +87 -0
- package/dist/tools/lint.js +104 -0
- package/dist/types.js +1 -0
- package/package.json +65 -0
|
@@ -0,0 +1,756 @@
|
|
|
1
|
+
# Bumble (iOS) — Jetpack Compose Implementation Guide
|
|
2
|
+
|
|
3
|
+
Companion to [DESIGN.md](DESIGN.md) — the framework-neutral spec. This file ports Bumble's visual language to **Android with Jetpack Compose (Material 3)**: a color token object, a `Typography` set, paste-ready `@Composable`s, the `Hexagon` shape, the glowing heart action button, the It's-a-Match celebration, and the 24-hour countdown chip.
|
|
4
|
+
|
|
5
|
+
> Why a Compose guide for an iOS-referenced app? The DESIGN.md tokens are platform-neutral. This file keeps the *visual* identity (Bumble's stadium-loud yellow, pure-black-on-yellow contrast, the hexagon spatial signature, the heavy Brando ladder) while making everything idiomatic Android — a custom `Shape` for the hex, `NavigationBar` instead of a UITabBar, `Brush` glow approximated with elevation + a radial scrim, `sp`/`dp` instead of `pt`.
|
|
6
|
+
|
|
7
|
+
Assumes Compose BOM `2024.09+`, Kotlin `2.0+`, `minSdk 24`, Material 3, and [Coil](https://coil-kt.github.io/coil/) `2.6+` for profile photos. No color extraction, so no `androidx.palette`.
|
|
8
|
+
|
|
9
|
+
## 1. Color Tokens
|
|
10
|
+
|
|
11
|
+
```kotlin
|
|
12
|
+
// ui/theme/BumbleColors.kt
|
|
13
|
+
import androidx.compose.ui.graphics.Color
|
|
14
|
+
|
|
15
|
+
object BumbleColors {
|
|
16
|
+
// Brand
|
|
17
|
+
val Yellow = Color(0xFFFFC629) // the brand — CTAs, tab indicator, match, heart
|
|
18
|
+
val HoneyDeep = Color(0xFFF5B616) // pressed state on yellow
|
|
19
|
+
val YellowLight = Color(0xFFFFE9A1) // soft chip fills, premium gradients
|
|
20
|
+
|
|
21
|
+
// Mode colors (Date / BFF / Bizz)
|
|
22
|
+
val BFFTeal = Color(0xFF11AAA8)
|
|
23
|
+
val BizzOrange = Color(0xFFFF8000)
|
|
24
|
+
|
|
25
|
+
// Canvas & Surfaces
|
|
26
|
+
val Canvas = Color(0xFFFFFFFF) // Date mode
|
|
27
|
+
val BFFCream = Color(0xFFFFFCF2) // BFF mode tint
|
|
28
|
+
val Surface1 = Color(0xFFF5F5F5) // input fills, settings rows
|
|
29
|
+
val Surface2 = Color(0xFFEDEDED) // pressed states, chip fills
|
|
30
|
+
val Divider = Color(0xFFE5E5E5) // 0.5dp hairlines
|
|
31
|
+
|
|
32
|
+
// Text
|
|
33
|
+
val Black = Color(0xFF1F1F1F) // primary on white (warm, NOT pure)
|
|
34
|
+
val Slate = Color(0xFF5A5A5A) // secondary
|
|
35
|
+
val Mist = Color(0xFF9C9C9C) // tertiary, placeholders
|
|
36
|
+
val OnYellow = Color(0xFF000000) // pure black — required on yellow for WCAG AA
|
|
37
|
+
|
|
38
|
+
// Semantic
|
|
39
|
+
val MatchPink = Color(0xFFE94B7B) // ONLY the It's-a-Match heart
|
|
40
|
+
val Verified = Color(0xFF0066FF) // verified checkmark
|
|
41
|
+
val Warning = Color(0xFFFF9500) // incomplete-profile warning
|
|
42
|
+
val Error = Color(0xFFD72638) // validation errors, expired countdown
|
|
43
|
+
val Success = Color(0xFF00A86B) // confirmation toasts
|
|
44
|
+
|
|
45
|
+
// Dark mode (warm OLED — yellow unchanged)
|
|
46
|
+
val DarkCanvas = Color(0xFF0F0F0F)
|
|
47
|
+
val DarkSurface1 = Color(0xFF1A1A1A)
|
|
48
|
+
val DarkSurface2 = Color(0xFF2A2A2A)
|
|
49
|
+
val DarkDivider = Color(0xFF2F2F2F)
|
|
50
|
+
val DarkText = Color(0xFFF2F2F2)
|
|
51
|
+
val DarkTextSec = Color(0xFF9C9C9C)
|
|
52
|
+
val HoneyDeepDk = Color(0xFFFFD45C) // brightened for OLED pressed states
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Wire it into a Material 3 `lightColorScheme` (Date mode) — Bumble's primary surface is white. Provide a parallel dark scheme; the yellow stays identical because it reads loud on OLED.
|
|
57
|
+
|
|
58
|
+
```kotlin
|
|
59
|
+
// ui/theme/Theme.kt
|
|
60
|
+
import androidx.compose.foundation.isSystemInDarkTheme
|
|
61
|
+
import androidx.compose.material3.darkColorScheme
|
|
62
|
+
import androidx.compose.material3.lightColorScheme
|
|
63
|
+
import androidx.compose.material3.MaterialTheme
|
|
64
|
+
|
|
65
|
+
private val BumbleLight = lightColorScheme(
|
|
66
|
+
primary = BumbleColors.Yellow,
|
|
67
|
+
onPrimary = BumbleColors.OnYellow, // pure black on yellow — non-negotiable
|
|
68
|
+
background = BumbleColors.Canvas,
|
|
69
|
+
onBackground = BumbleColors.Black,
|
|
70
|
+
surface = BumbleColors.Canvas,
|
|
71
|
+
onSurface = BumbleColors.Black,
|
|
72
|
+
surfaceVariant = BumbleColors.Surface1,
|
|
73
|
+
outline = BumbleColors.Divider,
|
|
74
|
+
error = BumbleColors.Error,
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
private val BumbleDark = darkColorScheme(
|
|
78
|
+
primary = BumbleColors.Yellow, // unchanged on dark
|
|
79
|
+
onPrimary = BumbleColors.OnYellow,
|
|
80
|
+
background = BumbleColors.DarkCanvas,
|
|
81
|
+
onBackground = BumbleColors.DarkText,
|
|
82
|
+
surface = BumbleColors.DarkSurface1,
|
|
83
|
+
onSurface = BumbleColors.DarkText,
|
|
84
|
+
surfaceVariant = BumbleColors.DarkSurface2,
|
|
85
|
+
outline = BumbleColors.DarkDivider,
|
|
86
|
+
error = BumbleColors.Error,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
@Composable
|
|
90
|
+
fun BumbleTheme(content: @Composable () -> Unit) =
|
|
91
|
+
MaterialTheme(
|
|
92
|
+
colorScheme = if (isSystemInDarkTheme()) BumbleDark else BumbleLight,
|
|
93
|
+
typography = BumbleTypography,
|
|
94
|
+
content = content,
|
|
95
|
+
)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## 2. Typography
|
|
99
|
+
|
|
100
|
+
Brando is the proprietary Bumble face — slab-influenced, humanist, heavy. Drop the TTFs in `res/font/` (lowercase, snake_case). Bumble has **no Light or Thin** — only 400/500/700/900. Fall back to Roboto (the closest free Android substitute); for a tighter brand match consider bundling Bricolage Grotesque / Manrope Heavy.
|
|
101
|
+
|
|
102
|
+
```kotlin
|
|
103
|
+
// ui/theme/BumbleType.kt
|
|
104
|
+
import androidx.compose.material3.Typography
|
|
105
|
+
import androidx.compose.ui.text.TextStyle
|
|
106
|
+
import androidx.compose.ui.text.font.Font
|
|
107
|
+
import androidx.compose.ui.text.font.FontFamily
|
|
108
|
+
import androidx.compose.ui.text.font.FontWeight
|
|
109
|
+
import androidx.compose.ui.unit.sp
|
|
110
|
+
|
|
111
|
+
val Brando = FontFamily(
|
|
112
|
+
Font(R.font.brando_regular, FontWeight.Normal), // 400
|
|
113
|
+
Font(R.font.brando_medium, FontWeight.Medium), // 500
|
|
114
|
+
Font(R.font.brando_bold, FontWeight.Bold), // 700
|
|
115
|
+
Font(R.font.brando_black, FontWeight.Black), // 900
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
// Named ramp — mirrors DESIGN.md §3 exactly (pt → sp 1:1, same weights/tracking)
|
|
119
|
+
object BumbleText {
|
|
120
|
+
val MatchHero = TextStyle(Brando, fontWeight = FontWeight.Black, fontSize = 44.sp, lineHeight = 46.sp, letterSpacing = (-0.8).sp)
|
|
121
|
+
val Display = TextStyle(Brando, fontWeight = FontWeight.Black, fontSize = 32.sp, lineHeight = 35.sp, letterSpacing = (-0.6).sp)
|
|
122
|
+
val ScreenTitle = TextStyle(Brando, fontWeight = FontWeight.Black, fontSize = 24.sp, lineHeight = 28.sp, letterSpacing = (-0.4).sp)
|
|
123
|
+
val CardName = TextStyle(Brando, fontWeight = FontWeight.Bold, fontSize = 28.sp, lineHeight = 31.sp, letterSpacing = (-0.4).sp)
|
|
124
|
+
val Section = TextStyle(Brando, fontWeight = FontWeight.Bold, fontSize = 18.sp, lineHeight = 22.sp, letterSpacing = (-0.1).sp)
|
|
125
|
+
val Body = TextStyle(Brando, fontWeight = FontWeight.Medium, fontSize = 16.sp, lineHeight = 22.sp)
|
|
126
|
+
val BodyBold = TextStyle(Brando, fontWeight = FontWeight.Bold, fontSize = 16.sp, lineHeight = 22.sp)
|
|
127
|
+
val BodySmall = TextStyle(Brando, fontWeight = FontWeight.Medium, fontSize = 14.sp, lineHeight = 20.sp)
|
|
128
|
+
val Button = TextStyle(Brando, fontWeight = FontWeight.Bold, fontSize = 16.sp, lineHeight = 16.sp)
|
|
129
|
+
val ButtonLarge = TextStyle(Brando, fontWeight = FontWeight.Bold, fontSize = 18.sp, lineHeight = 18.sp)
|
|
130
|
+
val Tab = TextStyle(Brando, fontWeight = FontWeight.Bold, fontSize = 10.sp, lineHeight = 10.sp, letterSpacing = 0.2.sp)
|
|
131
|
+
val Chip = TextStyle(Brando, fontWeight = FontWeight.Medium, fontSize = 13.sp, lineHeight = 13.sp)
|
|
132
|
+
val Meta = TextStyle(Brando, fontWeight = FontWeight.Medium, fontSize = 13.sp, lineHeight = 17.sp)
|
|
133
|
+
val Counter = TextStyle(Brando, fontWeight = FontWeight.Bold, fontSize = 11.sp, lineHeight = 11.sp, letterSpacing = 0.2.sp)
|
|
134
|
+
val Compliment = TextStyle(Brando, fontWeight = FontWeight.Black, fontSize = 22.sp, lineHeight = 26.sp, letterSpacing = (-0.3).sp)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Map onto Material 3 slots so stock components inherit the brand
|
|
138
|
+
val BumbleTypography = Typography(
|
|
139
|
+
displayLarge = BumbleText.Display,
|
|
140
|
+
headlineLarge = BumbleText.ScreenTitle,
|
|
141
|
+
titleLarge = BumbleText.CardName,
|
|
142
|
+
titleMedium = BumbleText.Section,
|
|
143
|
+
bodyMedium = BumbleText.Body,
|
|
144
|
+
labelSmall = BumbleText.Tab,
|
|
145
|
+
)
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
The 24-hour countdown is the only place numerals must hold column width — pin tabular figures on `Meta` (countdown) and `Counter` (badges) with `TextStyle(fontFeatureSettings = "tnum")`.
|
|
149
|
+
|
|
150
|
+
## 3. Signature Components
|
|
151
|
+
|
|
152
|
+
### Hexagon Shape (the brand spatial signature)
|
|
153
|
+
|
|
154
|
+
Bumble's hex is point-up. This is the SwiftUI `Hexagon: Shape` ported to a Compose `Shape` — reuse it for match avatars, mode-toggle buttons, and the brand mark.
|
|
155
|
+
|
|
156
|
+
```kotlin
|
|
157
|
+
import androidx.compose.ui.geometry.Offset
|
|
158
|
+
import androidx.compose.ui.graphics.Outline
|
|
159
|
+
import androidx.compose.ui.graphics.Path
|
|
160
|
+
import androidx.compose.ui.graphics.Shape
|
|
161
|
+
import androidx.compose.ui.unit.Density
|
|
162
|
+
import androidx.compose.ui.unit.LayoutDirection
|
|
163
|
+
import kotlin.math.cos
|
|
164
|
+
import kotlin.math.min
|
|
165
|
+
import kotlin.math.sin
|
|
166
|
+
|
|
167
|
+
val HexagonShape = object : Shape {
|
|
168
|
+
override fun createOutline(
|
|
169
|
+
size: androidx.compose.ui.geometry.Size,
|
|
170
|
+
layoutDirection: LayoutDirection,
|
|
171
|
+
density: Density,
|
|
172
|
+
): Outline {
|
|
173
|
+
val r = min(size.width, size.height) / 2f
|
|
174
|
+
val cx = size.width / 2f
|
|
175
|
+
val cy = size.height / 2f
|
|
176
|
+
val path = Path()
|
|
177
|
+
for (i in 0 until 6) {
|
|
178
|
+
val angle = (Math.PI / 3 * i - Math.PI / 2).toFloat() // point-up
|
|
179
|
+
val x = cx + r * cos(angle)
|
|
180
|
+
val y = cy + r * sin(angle)
|
|
181
|
+
if (i == 0) path.moveTo(x, y) else path.lineTo(x, y)
|
|
182
|
+
}
|
|
183
|
+
path.close()
|
|
184
|
+
return Outline.Generic(path)
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Yellow Primary CTA
|
|
190
|
+
|
|
191
|
+
```kotlin
|
|
192
|
+
import androidx.compose.animation.core.animateFloatAsState
|
|
193
|
+
import androidx.compose.animation.core.spring
|
|
194
|
+
import androidx.compose.foundation.background
|
|
195
|
+
import androidx.compose.foundation.interaction.MutableInteractionSource
|
|
196
|
+
import androidx.compose.foundation.interaction.collectIsPressedAsState
|
|
197
|
+
import androidx.compose.foundation.layout.*
|
|
198
|
+
import androidx.compose.foundation.shape.CircleShape
|
|
199
|
+
import androidx.compose.material3.Text
|
|
200
|
+
import androidx.compose.runtime.*
|
|
201
|
+
import androidx.compose.ui.Alignment
|
|
202
|
+
import androidx.compose.ui.Modifier
|
|
203
|
+
import androidx.compose.ui.draw.clip
|
|
204
|
+
import androidx.compose.ui.draw.scale
|
|
205
|
+
import androidx.compose.ui.draw.shadow
|
|
206
|
+
import androidx.compose.ui.graphics.Color
|
|
207
|
+
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
|
208
|
+
import androidx.compose.ui.platform.LocalHapticFeedback
|
|
209
|
+
import androidx.compose.foundation.clickable
|
|
210
|
+
import androidx.compose.ui.unit.dp
|
|
211
|
+
|
|
212
|
+
@Composable
|
|
213
|
+
fun BumblePrimaryButton(
|
|
214
|
+
label: String,
|
|
215
|
+
onClick: () -> Unit,
|
|
216
|
+
modifier: Modifier = Modifier,
|
|
217
|
+
hasGlow: Boolean = false,
|
|
218
|
+
) {
|
|
219
|
+
val interaction = remember { MutableInteractionSource() }
|
|
220
|
+
val pressed by interaction.collectIsPressedAsState()
|
|
221
|
+
val scale by animateFloatAsState(if (pressed) 0.97f else 1f, label = "ctaScale")
|
|
222
|
+
val haptics = LocalHapticFeedback.current
|
|
223
|
+
|
|
224
|
+
Box(
|
|
225
|
+
modifier = modifier
|
|
226
|
+
.fillMaxWidth()
|
|
227
|
+
.height(56.dp)
|
|
228
|
+
.scale(scale)
|
|
229
|
+
// Yellow glow halo — Android has no colored blur shadow, so a tinted
|
|
230
|
+
// ambient/spot shadow approximates the brand glow on hero CTAs.
|
|
231
|
+
.then(
|
|
232
|
+
if (hasGlow)
|
|
233
|
+
Modifier.shadow(24.dp, CircleShape, ambientColor = BumbleColors.Yellow,
|
|
234
|
+
spotColor = BumbleColors.Yellow)
|
|
235
|
+
else Modifier
|
|
236
|
+
)
|
|
237
|
+
.clip(CircleShape) // 28dp capsule == full pill at 56dp height
|
|
238
|
+
.background(if (pressed) BumbleColors.HoneyDeep else BumbleColors.Yellow)
|
|
239
|
+
.clickable(interaction, indication = null) {
|
|
240
|
+
haptics.performHapticFeedback(HapticFeedbackType.LongPress) // ≈ .impact(.medium)
|
|
241
|
+
onClick()
|
|
242
|
+
},
|
|
243
|
+
contentAlignment = Alignment.Center,
|
|
244
|
+
) {
|
|
245
|
+
Text(label, style = BumbleText.Button, color = BumbleColors.OnYellow) // pure black
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### Swipe Action Row (under the swipe card)
|
|
251
|
+
|
|
252
|
+
Five circular buttons; the yellow heart is the hero with a glow halo. Press scales to 0.9; the haptic varies per button.
|
|
253
|
+
|
|
254
|
+
```kotlin
|
|
255
|
+
import androidx.compose.foundation.border
|
|
256
|
+
import androidx.compose.material.icons.Icons
|
|
257
|
+
import androidx.compose.material.icons.filled.Close
|
|
258
|
+
import androidx.compose.material.icons.filled.Favorite
|
|
259
|
+
import androidx.compose.material.icons.filled.Star
|
|
260
|
+
import androidx.compose.material.icons.automirrored.filled.Undo
|
|
261
|
+
import androidx.compose.material3.Icon
|
|
262
|
+
import androidx.compose.ui.graphics.vector.ImageVector
|
|
263
|
+
|
|
264
|
+
@Composable
|
|
265
|
+
fun SwipeActionRow(
|
|
266
|
+
onRewind: () -> Unit,
|
|
267
|
+
onPass: () -> Unit,
|
|
268
|
+
onLike: () -> Unit,
|
|
269
|
+
onSuper: () -> Unit,
|
|
270
|
+
onCompliment: () -> Unit,
|
|
271
|
+
modifier: Modifier = Modifier,
|
|
272
|
+
) {
|
|
273
|
+
Row(
|
|
274
|
+
modifier.fillMaxWidth(),
|
|
275
|
+
horizontalArrangement = Arrangement.spacedBy(24.dp, Alignment.CenterHorizontally),
|
|
276
|
+
verticalAlignment = Alignment.CenterVertically,
|
|
277
|
+
) {
|
|
278
|
+
ActionCircle(48.dp, BumbleColors.Surface1, null, HapticFeedbackType.TextHandleMove, onRewind) {
|
|
279
|
+
Icon(Icons.AutoMirrored.Filled.Undo, "Rewind", tint = BumbleColors.Mist, modifier = Modifier.size(20.dp))
|
|
280
|
+
}
|
|
281
|
+
ActionCircle(56.dp, BumbleColors.Canvas, BumbleColors.Black, HapticFeedbackType.TextHandleMove, onPass) {
|
|
282
|
+
Icon(Icons.Filled.Close, "Pass", tint = BumbleColors.Black, modifier = Modifier.size(22.dp))
|
|
283
|
+
}
|
|
284
|
+
// Heart Yes — 64dp hero with yellow glow halo
|
|
285
|
+
HeartActionButton(onLike)
|
|
286
|
+
ActionCircle(56.dp, BumbleColors.Yellow, null, HapticFeedbackType.LongPress, onSuper) {
|
|
287
|
+
Icon(Icons.Filled.Star, "SuperSwipe", tint = BumbleColors.OnYellow, modifier = Modifier.size(22.dp))
|
|
288
|
+
}
|
|
289
|
+
ActionCircle(56.dp, BumbleColors.Canvas, BumbleColors.Yellow, HapticFeedbackType.TextHandleMove, onCompliment) {
|
|
290
|
+
// Bee glyph is proprietary — ship as a vector drawable; placeholder star here
|
|
291
|
+
Icon(Icons.Filled.Star, "Compliment", tint = BumbleColors.Yellow, modifier = Modifier.size(22.dp))
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
@Composable
|
|
297
|
+
private fun ActionCircle(
|
|
298
|
+
diameter: androidx.compose.ui.unit.Dp,
|
|
299
|
+
fill: Color,
|
|
300
|
+
stroke: Color?,
|
|
301
|
+
haptic: HapticFeedbackType,
|
|
302
|
+
onClick: () -> Unit,
|
|
303
|
+
content: @Composable () -> Unit,
|
|
304
|
+
) {
|
|
305
|
+
val interaction = remember { MutableInteractionSource() }
|
|
306
|
+
val pressed by interaction.collectIsPressedAsState()
|
|
307
|
+
val scale by animateFloatAsState(if (pressed) 0.9f else 1f, label = "actionScale")
|
|
308
|
+
val haptics = LocalHapticFeedback.current
|
|
309
|
+
Box(
|
|
310
|
+
Modifier
|
|
311
|
+
.size(diameter)
|
|
312
|
+
.scale(scale)
|
|
313
|
+
.clip(CircleShape)
|
|
314
|
+
.background(fill)
|
|
315
|
+
.then(if (stroke != null) Modifier.border(1.5.dp, stroke, CircleShape) else Modifier)
|
|
316
|
+
.clickable(interaction, indication = null) { haptics.performHapticFeedback(haptic); onClick() },
|
|
317
|
+
contentAlignment = Alignment.Center,
|
|
318
|
+
) { content() }
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
@Composable
|
|
322
|
+
private fun HeartActionButton(onLike: () -> Unit) {
|
|
323
|
+
val interaction = remember { MutableInteractionSource() }
|
|
324
|
+
val pressed by interaction.collectIsPressedAsState()
|
|
325
|
+
val scale by animateFloatAsState(
|
|
326
|
+
targetValue = if (pressed) 1.2f else 1f,
|
|
327
|
+
animationSpec = spring(dampingRatio = 0.6f, stiffness = 600f),
|
|
328
|
+
label = "heartScale",
|
|
329
|
+
)
|
|
330
|
+
val haptics = LocalHapticFeedback.current
|
|
331
|
+
Box(
|
|
332
|
+
Modifier
|
|
333
|
+
.size(64.dp)
|
|
334
|
+
.scale(scale)
|
|
335
|
+
.shadow(16.dp, CircleShape, ambientColor = BumbleColors.Yellow, spotColor = BumbleColors.Yellow)
|
|
336
|
+
.clip(CircleShape)
|
|
337
|
+
.background(BumbleColors.Yellow)
|
|
338
|
+
.clickable(interaction, indication = null) {
|
|
339
|
+
haptics.performHapticFeedback(HapticFeedbackType.LongPress) // ≈ .impact(.medium)
|
|
340
|
+
onLike()
|
|
341
|
+
},
|
|
342
|
+
contentAlignment = Alignment.Center,
|
|
343
|
+
) { Icon(Icons.Filled.Favorite, "Like", tint = BumbleColors.OnYellow, modifier = Modifier.size(28.dp)) }
|
|
344
|
+
}
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
### Swipe Card (the hero component)
|
|
348
|
+
|
|
349
|
+
3:4 photo, 12dp radius (firmer edge than Hinge), 4-segment Stories-style progress bar at the top, bottom gradient carrying name + verified check + bio.
|
|
350
|
+
|
|
351
|
+
```kotlin
|
|
352
|
+
import androidx.compose.foundation.gestures.detectHorizontalDragGestures
|
|
353
|
+
import androidx.compose.material.icons.filled.Verified
|
|
354
|
+
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
355
|
+
import androidx.compose.ui.graphics.Brush
|
|
356
|
+
import androidx.compose.ui.input.pointer.pointerInput
|
|
357
|
+
import androidx.compose.ui.layout.ContentScale
|
|
358
|
+
import androidx.compose.ui.text.style.TextOverflow
|
|
359
|
+
import coil.compose.AsyncImage
|
|
360
|
+
|
|
361
|
+
@Composable
|
|
362
|
+
fun SwipeCard(
|
|
363
|
+
photoUrls: List<String>,
|
|
364
|
+
name: String,
|
|
365
|
+
age: Int,
|
|
366
|
+
bio: String,
|
|
367
|
+
isVerified: Boolean,
|
|
368
|
+
onSwiped: (liked: Boolean) -> Unit,
|
|
369
|
+
modifier: Modifier = Modifier,
|
|
370
|
+
) {
|
|
371
|
+
var current by remember { mutableStateOf(0) }
|
|
372
|
+
var dragX by remember { mutableStateOf(0f) }
|
|
373
|
+
|
|
374
|
+
Box(
|
|
375
|
+
modifier = modifier
|
|
376
|
+
.fillMaxWidth()
|
|
377
|
+
.aspectRatio(3f / 4f)
|
|
378
|
+
.shadow(16.dp, RoundedCornerShape(12.dp), spotColor = Color.Black.copy(alpha = 0.12f))
|
|
379
|
+
.clip(RoundedCornerShape(12.dp))
|
|
380
|
+
.pointerInput(Unit) {
|
|
381
|
+
detectHorizontalDragGestures(
|
|
382
|
+
onDragEnd = { if (kotlin.math.abs(dragX) > size.width * 0.3f) onSwiped(dragX > 0); dragX = 0f },
|
|
383
|
+
) { _, drag -> dragX += drag }
|
|
384
|
+
},
|
|
385
|
+
) {
|
|
386
|
+
AsyncImage(
|
|
387
|
+
model = photoUrls[current],
|
|
388
|
+
contentDescription = "$name, $age",
|
|
389
|
+
modifier = Modifier.fillMaxSize(),
|
|
390
|
+
contentScale = ContentScale.Crop,
|
|
391
|
+
)
|
|
392
|
+
// Bottom gradient overlay (bottom 30%, transparent → 0.7 black)
|
|
393
|
+
Box(
|
|
394
|
+
Modifier
|
|
395
|
+
.align(Alignment.BottomCenter)
|
|
396
|
+
.fillMaxWidth()
|
|
397
|
+
.fillMaxHeight(0.3f)
|
|
398
|
+
.background(Brush.verticalGradient(listOf(Color.Transparent, Color.Black.copy(alpha = 0.7f))))
|
|
399
|
+
)
|
|
400
|
+
// Photo progress bar — 4 segments, 3dp tall, 4dp gap
|
|
401
|
+
Row(
|
|
402
|
+
Modifier.fillMaxWidth().padding(12.dp),
|
|
403
|
+
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
|
404
|
+
) {
|
|
405
|
+
repeat(photoUrls.size) { i ->
|
|
406
|
+
Box(
|
|
407
|
+
Modifier
|
|
408
|
+
.weight(1f)
|
|
409
|
+
.height(3.dp)
|
|
410
|
+
.clip(CircleShape)
|
|
411
|
+
.background(if (i == current) Color.White else Color.White.copy(alpha = 0.4f))
|
|
412
|
+
)
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
// Name + age + verified + bio
|
|
416
|
+
Column(
|
|
417
|
+
Modifier.align(Alignment.BottomStart).padding(horizontal = 20.dp, vertical = 20.dp),
|
|
418
|
+
verticalArrangement = Arrangement.spacedBy(4.dp),
|
|
419
|
+
) {
|
|
420
|
+
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
|
421
|
+
Text("$name, $age", style = BumbleText.CardName, color = Color.White)
|
|
422
|
+
if (isVerified) {
|
|
423
|
+
Icon(Icons.Filled.Verified, "Verified", tint = BumbleColors.Verified, modifier = Modifier.size(18.dp))
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
Text(bio, style = BumbleText.Body, color = Color.White, maxLines = 1, overflow = TextOverflow.Ellipsis)
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
### 24-Hour Countdown Chip (the core mechanic)
|
|
433
|
+
|
|
434
|
+
```kotlin
|
|
435
|
+
import androidx.compose.animation.core.RepeatMode
|
|
436
|
+
import androidx.compose.animation.core.infiniteRepeatable
|
|
437
|
+
import androidx.compose.animation.core.rememberInfiniteTransition
|
|
438
|
+
import androidx.compose.animation.core.tween
|
|
439
|
+
import androidx.compose.ui.draw.alpha
|
|
440
|
+
|
|
441
|
+
@Composable
|
|
442
|
+
fun CountdownChip(remainingSeconds: Long, modifier: Modifier = Modifier) {
|
|
443
|
+
val expired = remainingSeconds <= 0
|
|
444
|
+
val label = if (expired) "Time's up — extend?"
|
|
445
|
+
else "Your turn: ${remainingSeconds / 3600}h ${(remainingSeconds % 3600) / 60}m"
|
|
446
|
+
|
|
447
|
+
val t = rememberInfiniteTransition(label = "countdownPulse")
|
|
448
|
+
val a by t.animateFloat(
|
|
449
|
+
initialValue = 0.95f, targetValue = 1f,
|
|
450
|
+
animationSpec = infiniteRepeatable(tween(1500), RepeatMode.Reverse),
|
|
451
|
+
label = "pulse",
|
|
452
|
+
)
|
|
453
|
+
|
|
454
|
+
Box(
|
|
455
|
+
modifier
|
|
456
|
+
.fillMaxWidth()
|
|
457
|
+
.height(32.dp)
|
|
458
|
+
.alpha(a)
|
|
459
|
+
.clip(CircleShape)
|
|
460
|
+
.background(if (expired) BumbleColors.Error else BumbleColors.Yellow)
|
|
461
|
+
.border(1.dp, if (expired) BumbleColors.Error else BumbleColors.HoneyDeep, CircleShape)
|
|
462
|
+
.padding(horizontal = 16.dp),
|
|
463
|
+
contentAlignment = Alignment.Center,
|
|
464
|
+
) {
|
|
465
|
+
Text(
|
|
466
|
+
label,
|
|
467
|
+
style = BumbleText.Meta.copy(fontWeight = FontWeight.Bold, fontFeatureSettings = "tnum"),
|
|
468
|
+
color = if (expired) Color.White else BumbleColors.OnYellow, // pure black on yellow
|
|
469
|
+
)
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
### It's a Match! Celebration
|
|
475
|
+
|
|
476
|
+
```kotlin
|
|
477
|
+
import androidx.compose.animation.core.Animatable
|
|
478
|
+
import androidx.compose.ui.draw.scale
|
|
479
|
+
|
|
480
|
+
@Composable
|
|
481
|
+
fun MatchCelebration(
|
|
482
|
+
myAvatarUrl: String,
|
|
483
|
+
theirAvatarUrl: String,
|
|
484
|
+
theirName: String,
|
|
485
|
+
onSendMessage: () -> Unit,
|
|
486
|
+
onKeepSwiping: () -> Unit,
|
|
487
|
+
) {
|
|
488
|
+
val heartScale = remember { Animatable(0.2f) }
|
|
489
|
+
val haptics = LocalHapticFeedback.current
|
|
490
|
+
|
|
491
|
+
LaunchedEffect(Unit) {
|
|
492
|
+
haptics.performHapticFeedback(HapticFeedbackType.LongPress) // ≈ .notification(.success)
|
|
493
|
+
heartScale.animateTo(1f, spring(dampingRatio = 0.55f, stiffness = 500f)) // bounce in
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
Box(Modifier.fillMaxSize().background(BumbleColors.Yellow)) { // full-screen yellow canvas
|
|
497
|
+
Column(
|
|
498
|
+
Modifier.fillMaxSize().padding(horizontal = 24.dp),
|
|
499
|
+
horizontalAlignment = Alignment.CenterHorizontally,
|
|
500
|
+
verticalArrangement = Arrangement.Center,
|
|
501
|
+
) {
|
|
502
|
+
Box(contentAlignment = Alignment.Center) {
|
|
503
|
+
Row(horizontalArrangement = Arrangement.spacedBy(32.dp)) {
|
|
504
|
+
HexAvatar(myAvatarUrl)
|
|
505
|
+
HexAvatar(theirAvatarUrl)
|
|
506
|
+
}
|
|
507
|
+
Icon(
|
|
508
|
+
Icons.Filled.Favorite, contentDescription = null,
|
|
509
|
+
tint = BumbleColors.MatchPink, // ONLY place this pink appears
|
|
510
|
+
modifier = Modifier.size(48.dp).scale(heartScale.value),
|
|
511
|
+
)
|
|
512
|
+
}
|
|
513
|
+
Spacer(Modifier.height(24.dp))
|
|
514
|
+
Text("It's a Match!", style = BumbleText.MatchHero, color = BumbleColors.Black)
|
|
515
|
+
Text("You and $theirName want to chat", style = BumbleText.Body, color = BumbleColors.Black)
|
|
516
|
+
Spacer(Modifier.height(12.dp))
|
|
517
|
+
Text("She has 24 hours to make the first move", style = BumbleText.Meta,
|
|
518
|
+
color = BumbleColors.Black.copy(alpha = 0.8f))
|
|
519
|
+
Spacer(Modifier.height(48.dp))
|
|
520
|
+
// Primary CTA is WHITE here (yellow canvas) with warm-black text
|
|
521
|
+
Box(
|
|
522
|
+
Modifier.fillMaxWidth().height(56.dp).clip(CircleShape).background(Color.White)
|
|
523
|
+
.clickable { onSendMessage() },
|
|
524
|
+
contentAlignment = Alignment.Center,
|
|
525
|
+
) { Text("Send a Message", style = BumbleText.ButtonLarge, color = BumbleColors.Black) }
|
|
526
|
+
Spacer(Modifier.height(16.dp))
|
|
527
|
+
Text("Keep Swiping", style = BumbleText.Button, color = BumbleColors.Black,
|
|
528
|
+
modifier = Modifier.clickable { onKeepSwiping() })
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
@Composable
|
|
534
|
+
fun HexAvatar(url: String, size: androidx.compose.ui.unit.Dp = 120.dp) {
|
|
535
|
+
AsyncImage(
|
|
536
|
+
model = url,
|
|
537
|
+
contentDescription = null,
|
|
538
|
+
modifier = Modifier
|
|
539
|
+
.size(size)
|
|
540
|
+
.clip(HexagonShape)
|
|
541
|
+
.border(4.dp, Color.White, HexagonShape),
|
|
542
|
+
contentScale = ContentScale.Crop,
|
|
543
|
+
)
|
|
544
|
+
}
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
### Chat Input with Send Button
|
|
548
|
+
|
|
549
|
+
```kotlin
|
|
550
|
+
import androidx.compose.foundation.text.BasicTextField
|
|
551
|
+
import androidx.compose.material.icons.filled.ArrowUpward
|
|
552
|
+
import androidx.compose.ui.text.input.TextFieldValue
|
|
553
|
+
|
|
554
|
+
@Composable
|
|
555
|
+
fun BumbleChatInput(
|
|
556
|
+
value: TextFieldValue,
|
|
557
|
+
onValueChange: (TextFieldValue) -> Unit,
|
|
558
|
+
onSend: () -> Unit,
|
|
559
|
+
modifier: Modifier = Modifier,
|
|
560
|
+
) {
|
|
561
|
+
val canSend = value.text.isNotBlank()
|
|
562
|
+
val haptics = LocalHapticFeedback.current
|
|
563
|
+
Row(
|
|
564
|
+
modifier.fillMaxWidth().padding(16.dp),
|
|
565
|
+
verticalAlignment = Alignment.CenterVertically,
|
|
566
|
+
horizontalArrangement = Arrangement.spacedBy(10.dp),
|
|
567
|
+
) {
|
|
568
|
+
Box(
|
|
569
|
+
Modifier.weight(1f).clip(CircleShape).background(BumbleColors.Surface1)
|
|
570
|
+
.padding(horizontal = 20.dp, vertical = 14.dp),
|
|
571
|
+
) {
|
|
572
|
+
BasicTextField(
|
|
573
|
+
value = value, onValueChange = onValueChange,
|
|
574
|
+
textStyle = BumbleText.Body.copy(color = BumbleColors.Black),
|
|
575
|
+
decorationBox = { inner ->
|
|
576
|
+
if (value.text.isEmpty()) {
|
|
577
|
+
Text("Type your message…", style = BumbleText.Body, color = BumbleColors.Mist)
|
|
578
|
+
}
|
|
579
|
+
inner()
|
|
580
|
+
},
|
|
581
|
+
)
|
|
582
|
+
}
|
|
583
|
+
Box(
|
|
584
|
+
Modifier
|
|
585
|
+
.size(40.dp)
|
|
586
|
+
.clip(CircleShape)
|
|
587
|
+
.background(if (canSend) BumbleColors.Yellow else BumbleColors.Surface1)
|
|
588
|
+
.clickable(enabled = canSend) {
|
|
589
|
+
haptics.performHapticFeedback(HapticFeedbackType.LongPress)
|
|
590
|
+
onSend()
|
|
591
|
+
},
|
|
592
|
+
contentAlignment = Alignment.Center,
|
|
593
|
+
) {
|
|
594
|
+
Icon(Icons.Filled.ArrowUpward, "Send",
|
|
595
|
+
tint = if (canSend) BumbleColors.OnYellow else BumbleColors.Mist,
|
|
596
|
+
modifier = Modifier.size(18.dp))
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
## 4. Mode Switching (the distinctive system)
|
|
603
|
+
|
|
604
|
+
Bumble's defining structural feature is one app, three products: Date (yellow) / BFF (teal) / Bizz (orange). Switching modes recolors **all** brand chrome while the hexagon shape stays constant. Drive this with a `CompositionLocal` so every composable reads the active accent without prop-drilling.
|
|
605
|
+
|
|
606
|
+
```kotlin
|
|
607
|
+
enum class BumbleMode(val accent: Color, val pressed: Color) {
|
|
608
|
+
Date(BumbleColors.Yellow, BumbleColors.HoneyDeep),
|
|
609
|
+
BFF(BumbleColors.BFFTeal, Color(0xFF0E908E)),
|
|
610
|
+
Bizz(BumbleColors.BizzOrange, Color(0xFFE57300)),
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
val LocalBumbleMode = androidx.compose.runtime.staticCompositionLocalOf { BumbleMode.Date }
|
|
614
|
+
|
|
615
|
+
@Composable
|
|
616
|
+
fun BumbleModeProvider(mode: BumbleMode, content: @Composable () -> Unit) {
|
|
617
|
+
androidx.compose.runtime.CompositionLocalProvider(LocalBumbleMode provides mode, content = content)
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
// A hexagonal mode-toggle button — literally hex-shaped, not rectangular
|
|
621
|
+
@Composable
|
|
622
|
+
fun ModeHexButton(mode: BumbleMode, active: Boolean, onClick: () -> Unit) {
|
|
623
|
+
val haptics = LocalHapticFeedback.current
|
|
624
|
+
Box(
|
|
625
|
+
Modifier
|
|
626
|
+
.size(64.dp)
|
|
627
|
+
.clip(HexagonShape)
|
|
628
|
+
.then(
|
|
629
|
+
if (active) Modifier.background(mode.accent)
|
|
630
|
+
else Modifier.border(2.dp, BumbleColors.Mist, HexagonShape)
|
|
631
|
+
)
|
|
632
|
+
.clickable {
|
|
633
|
+
haptics.performHapticFeedback(HapticFeedbackType.TextHandleMove) // ≈ .selection
|
|
634
|
+
onClick()
|
|
635
|
+
},
|
|
636
|
+
contentAlignment = Alignment.Center,
|
|
637
|
+
) {
|
|
638
|
+
Icon(Icons.Filled.Star, mode.name, // mode glyph (hive/heart/briefcase) — vector drawable in prod
|
|
639
|
+
tint = if (active) BumbleColors.OnYellow else BumbleColors.Mist, modifier = Modifier.size(24.dp))
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
Then read `LocalBumbleMode.current.accent` anywhere yellow appears (CTAs, tab indicator, match ring) so the whole app retints on switch. The hex is never replaced with a circle.
|
|
645
|
+
|
|
646
|
+
## 5. Navigation
|
|
647
|
+
|
|
648
|
+
Bumble has 5 bottom tabs: People / Hives / Matches / Chats / Profile — the Hives icon is a literal honeycomb. Use Material 3 `NavigationBar`. The iOS tab bar is opaque (Bumble likes hard surfaces, no blur) — Android has no live blur anyway, so use a solid `Canvas` container with a 0.5dp top divider. **Active tint is warm Bumble Black** with a 4dp yellow indicator dot below the label — not the Material pill.
|
|
649
|
+
|
|
650
|
+
```kotlin
|
|
651
|
+
import androidx.compose.material.icons.filled.Chat
|
|
652
|
+
import androidx.compose.material.icons.filled.Hexagon
|
|
653
|
+
import androidx.compose.material.icons.filled.People
|
|
654
|
+
import androidx.compose.material.icons.filled.Person
|
|
655
|
+
import androidx.compose.material3.NavigationBar
|
|
656
|
+
import androidx.compose.material3.NavigationBarItem
|
|
657
|
+
import androidx.compose.material3.NavigationBarItemDefaults
|
|
658
|
+
|
|
659
|
+
@Composable
|
|
660
|
+
fun BumbleBottomBar(selected: Int, onSelect: (Int) -> Unit) {
|
|
661
|
+
val accent = LocalBumbleMode.current.accent
|
|
662
|
+
NavigationBar(containerColor = BumbleColors.Canvas, tonalElevation = 0.dp) {
|
|
663
|
+
val items = listOf(
|
|
664
|
+
"People" to Icons.Filled.People,
|
|
665
|
+
"Hives" to Icons.Filled.Hexagon,
|
|
666
|
+
"Matches" to Icons.Filled.Favorite,
|
|
667
|
+
"Chats" to Icons.Filled.Chat,
|
|
668
|
+
"Profile" to Icons.Filled.Person,
|
|
669
|
+
)
|
|
670
|
+
items.forEachIndexed { i, (label, icon) ->
|
|
671
|
+
NavigationBarItem(
|
|
672
|
+
selected = selected == i,
|
|
673
|
+
onClick = { onSelect(i) },
|
|
674
|
+
icon = { Icon(icon, contentDescription = label, modifier = Modifier.size(24.dp)) },
|
|
675
|
+
label = { Text(label, style = BumbleText.Tab) },
|
|
676
|
+
colors = NavigationBarItemDefaults.colors(
|
|
677
|
+
selectedIconColor = BumbleColors.Black, // warm black, not yellow
|
|
678
|
+
selectedTextColor = BumbleColors.Black,
|
|
679
|
+
unselectedIconColor = BumbleColors.Mist,
|
|
680
|
+
unselectedTextColor = BumbleColors.Mist,
|
|
681
|
+
indicatorColor = accent.copy(alpha = 0.0f), // no Material pill — Bumble has none
|
|
682
|
+
),
|
|
683
|
+
)
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
```
|
|
688
|
+
|
|
689
|
+
Render the 4dp yellow (or mode-accent) indicator dot manually 4dp beneath the selected item's label if you want exact parity — `NavigationBar` has no built-in dot affordance. The top header is a slim 56dp row (no large-title) with `ScreenTitle` text leading and trailing action glyphs.
|
|
690
|
+
|
|
691
|
+
## 6. Motion
|
|
692
|
+
|
|
693
|
+
| Moment | Compose recipe |
|
|
694
|
+
|--------|----------------|
|
|
695
|
+
| Heart tap | `animateFloatAsState` 1 → 1.2 with `spring(dampingRatio = 0.6f)`; `HapticFeedbackType.LongPress` (.impact medium) |
|
|
696
|
+
| SuperSwipe (star) | full-screen yellow flash 200ms via `Animatable` alpha 0→1→0; `HapticFeedbackType.LongPress` (.success) |
|
|
697
|
+
| Swipe commit | `animateFloatAsState` `rotationZ` → 8° + `offset` 500dp off-screen over 350ms (`spring`) |
|
|
698
|
+
| Yes/No confetti | `rememberInfiniteTransition` / `Animatable` driving falling yellow hexes for 800ms |
|
|
699
|
+
| Match celebration | photos slide in from edges; pink heart `Animatable.animateTo(1f, spring(dampingRatio = 0.55f))` |
|
|
700
|
+
| 24h chip pulse | `rememberInfiniteTransition` alpha 0.95 → 1.0 over 1500ms `RepeatMode.Reverse` |
|
|
701
|
+
| Hex loading state | `rememberInfiniteTransition` `rotationZ` 0 → 360 over 1200ms on a 6-hex honeycomb |
|
|
702
|
+
| Tab switch | 200ms `tween`; indicator dot `fadeIn`; `HapticFeedbackType.TextHandleMove` (.selection) |
|
|
703
|
+
|
|
704
|
+
```kotlin
|
|
705
|
+
// SuperSwipe full-screen yellow flash
|
|
706
|
+
@Composable
|
|
707
|
+
fun SuperSwipeFlash(trigger: Boolean) {
|
|
708
|
+
val flash = remember { Animatable(0f) }
|
|
709
|
+
val haptics = LocalHapticFeedback.current
|
|
710
|
+
LaunchedEffect(trigger) {
|
|
711
|
+
if (trigger) {
|
|
712
|
+
haptics.performHapticFeedback(HapticFeedbackType.LongPress) // ≈ .notification(.success)
|
|
713
|
+
flash.animateTo(1f, tween(200))
|
|
714
|
+
flash.animateTo(0f, tween(200))
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
if (flash.value > 0f) {
|
|
718
|
+
Box(Modifier.fillMaxSize().alpha(flash.value).background(BumbleColors.Yellow))
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
```
|
|
722
|
+
|
|
723
|
+
Honor **Reduce Motion**: when `Settings.Global.ANIMATOR_DURATION_SCALE == 0f`, skip the hex confetti and the heart scale-bounce on match — keep the haptics. For richer haptics use `LocalView.current.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)` (API 30+) or a `Vibrator` `VibrationEffect.createOneShot(10, ...)` to approximate iOS's `.soft` Compliment tap. For the match-celebration shared photo, prefer `SharedTransitionLayout` + `Modifier.sharedElement()` (Compose 1.7+).
|
|
724
|
+
|
|
725
|
+
## 7. Icons
|
|
726
|
+
|
|
727
|
+
Bumble uses outline/filled pairs; the closest first-party set is `androidx.compose.material:material-icons-extended`. The **bee glyph** and the **hexagonal "B" logomark** are proprietary — ship them as vector drawables and load via `ImageVector.vectorResource(R.drawable.…)`.
|
|
728
|
+
|
|
729
|
+
| Purpose | SF Symbol (iOS) | Material Icon (Compose) |
|
|
730
|
+
|---------|-----------------|--------------------------|
|
|
731
|
+
| Heart action (filled) | `heart.fill` | `Icons.Filled.Favorite` |
|
|
732
|
+
| X Pass | `xmark` | `Icons.Filled.Close` |
|
|
733
|
+
| Star SuperSwipe | `star.fill` | `Icons.Filled.Star` |
|
|
734
|
+
| Rewind | `arrow.uturn.backward` | `Icons.AutoMirrored.Filled.Undo` |
|
|
735
|
+
| Bee Compliment | `ant.fill` (placeholder) | custom vector drawable (proprietary bee) |
|
|
736
|
+
| Match heart | `heart.fill` (Match Pink) | `Icons.Filled.Favorite` tinted `MatchPink` |
|
|
737
|
+
| Send (chat input) | `arrow.up` | `Icons.Filled.ArrowUpward` |
|
|
738
|
+
| Verified check | `checkmark.seal.fill` | `Icons.Filled.Verified` (tint `Verified`) |
|
|
739
|
+
| People tab | `person.2` / `.fill` | `Icons.Filled.People` |
|
|
740
|
+
| Hives tab | `hexagon` / `.fill` | `Icons.Filled.Hexagon` |
|
|
741
|
+
| Matches tab | `heart` / `.fill` | `Icons.Filled.Favorite` |
|
|
742
|
+
| Chats tab | `bubble.left` / `.fill` | `Icons.Filled.Chat` |
|
|
743
|
+
| Profile tab | `person.crop.circle` / `.fill` | `Icons.Filled.Person` |
|
|
744
|
+
| Filters | `slider.horizontal.3` | `Icons.Filled.Tune` |
|
|
745
|
+
| Settings | `gearshape` | `Icons.Filled.Settings` |
|
|
746
|
+
| Brand "B" logomark | (hex bee mark) | custom vector drawable |
|
|
747
|
+
|
|
748
|
+
## 8. Minimum SDK & Accessibility Notes
|
|
749
|
+
|
|
750
|
+
- **minSdk 24** (Compose floor is 21; modern motion + `SharedTransitionLayout` comfortable at 24). `compileSdk 34+`, Compose BOM `2024.09+`, Kotlin `2.0+`.
|
|
751
|
+
- **Edge-to-edge**: call `enableEdgeToEdge()` in the `Activity`. The light Date canvas wants dark system-bar icons (`WindowCompat` light appearance); the dark scheme wants light icons. Pin the chat input above the IME with `Modifier.imePadding()` and the tab bar above the gesture nav with `Scaffold` insets.
|
|
752
|
+
- **Font scaling**: `sp` honors the user's font scale automatically — keep it on match hero, display, screen titles, body, bio, section headers. Pin layout-sensitive text (10sp tab labels, 11sp counter badges, 13sp chip labels, the 24-hour countdown) by deriving size from `dp` or wrapping in `CompositionLocalProvider(LocalDensity provides Density(density, fontScale = 1f))`.
|
|
753
|
+
- **TalkBack**: announce the match screen as one node — `Modifier.semantics(mergeDescendants = true) { contentDescription = "It's a Match. You and $name want to chat. She has 24 hours. Send a Message button." }`. Give the swipe card an accessibility action set (`"Like"`, `"Pass"`, `"SuperSwipe"`, `"Compliment"`) so users can act without dragging. Re-announce the countdown chip when it crosses a 30-minute boundary.
|
|
754
|
+
- **Touch targets**: Material guidance is 48.dp minimum. The 64dp heart and 56dp CTA clear it; the 48dp Rewind and 40dp send button are at/above the floor — keep the send button's 40dp visual inside a 48dp hit area via padding.
|
|
755
|
+
- **Contrast**: Bumble Yellow `#FFC629` requires **pure black** (`#000000`) text — warm `#1F1F1F` on yellow *fails* WCAG AA at small sizes. Use `BumbleColors.OnYellow` on every yellow surface, no exceptions. `#9C9C9C` Mist on white is borderline at body size — keep it to ≥14sp metadata and validate with a contrast checker.
|
|
756
|
+
- **Dynamic color**: do **not** enable Material You `dynamicColorScheme()` — Bumble's identity is the fixed `#FFC629` yellow (and the teal/orange mode accents) regardless of wallpaper. (Material You suits Google/Material-first apps; Bumble is a strong-brand exception.) The yellow stays identical across light and dark; only canvas/surface/text tokens invert.
|