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.
Files changed (907) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +119 -0
  3. package/dist/__tests__/designStore.test.js +41 -0
  4. package/dist/__tests__/linter.test.js +23 -0
  5. package/dist/__tests__/sectionParser.test.js +60 -0
  6. package/dist/__tests__/server.test.js +129 -0
  7. package/dist/constants.js +12 -0
  8. package/dist/data/authoring-guide.md +113 -0
  9. package/dist/data/designs/airbnb.md +545 -0
  10. package/dist/data/designs/airtable.md +554 -0
  11. package/dist/data/designs/apple.md +562 -0
  12. package/dist/data/designs/binance.md +634 -0
  13. package/dist/data/designs/bmw-m.md +503 -0
  14. package/dist/data/designs/bmw.md +544 -0
  15. package/dist/data/designs/bugatti.md +454 -0
  16. package/dist/data/designs/cal.md +542 -0
  17. package/dist/data/designs/card-heavy.md +250 -0
  18. package/dist/data/designs/claude.md +589 -0
  19. package/dist/data/designs/clay.md +541 -0
  20. package/dist/data/designs/clickhouse.md +544 -0
  21. package/dist/data/designs/cohere.md +451 -0
  22. package/dist/data/designs/coinbase.md +570 -0
  23. package/dist/data/designs/composio.md +506 -0
  24. package/dist/data/designs/content-first.md +236 -0
  25. package/dist/data/designs/cursor.md +537 -0
  26. package/dist/data/designs/data-dashboard.md +300 -0
  27. package/dist/data/designs/dell-1996.md +632 -0
  28. package/dist/data/designs/elevenlabs.md +504 -0
  29. package/dist/data/designs/enterprise-neutral.md +252 -0
  30. package/dist/data/designs/expo.md +526 -0
  31. package/dist/data/designs/ferrari.md +531 -0
  32. package/dist/data/designs/figma.md +578 -0
  33. package/dist/data/designs/framer.md +544 -0
  34. package/dist/data/designs/hashicorp.md +575 -0
  35. package/dist/data/designs/hp.md +670 -0
  36. package/dist/data/designs/ibm.md +550 -0
  37. package/dist/data/designs/intercom.md +546 -0
  38. package/dist/data/designs/ios-acorns-android.md +430 -0
  39. package/dist/data/designs/ios-acorns-expo.md +381 -0
  40. package/dist/data/designs/ios-acorns-swiftui.md +378 -0
  41. package/dist/data/designs/ios-acorns.md +384 -0
  42. package/dist/data/designs/ios-airbnb-android.md +658 -0
  43. package/dist/data/designs/ios-airbnb-expo.md +457 -0
  44. package/dist/data/designs/ios-airbnb-swiftui.md +464 -0
  45. package/dist/data/designs/ios-airbnb.md +410 -0
  46. package/dist/data/designs/ios-alltrails-android.md +451 -0
  47. package/dist/data/designs/ios-alltrails-expo.md +400 -0
  48. package/dist/data/designs/ios-alltrails-swiftui.md +343 -0
  49. package/dist/data/designs/ios-alltrails.md +344 -0
  50. package/dist/data/designs/ios-amazon-android.md +686 -0
  51. package/dist/data/designs/ios-amazon-expo.md +433 -0
  52. package/dist/data/designs/ios-amazon-music-android.md +416 -0
  53. package/dist/data/designs/ios-amazon-music-expo.md +368 -0
  54. package/dist/data/designs/ios-amazon-music-swiftui.md +436 -0
  55. package/dist/data/designs/ios-amazon-music.md +389 -0
  56. package/dist/data/designs/ios-amazon-swiftui.md +460 -0
  57. package/dist/data/designs/ios-amazon.md +440 -0
  58. package/dist/data/designs/ios-apple-fitness-android.md +462 -0
  59. package/dist/data/designs/ios-apple-fitness-expo.md +373 -0
  60. package/dist/data/designs/ios-apple-fitness-swiftui.md +373 -0
  61. package/dist/data/designs/ios-apple-fitness.md +399 -0
  62. package/dist/data/designs/ios-apple-maps-android.md +646 -0
  63. package/dist/data/designs/ios-apple-maps-expo.md +557 -0
  64. package/dist/data/designs/ios-apple-maps-swiftui.md +588 -0
  65. package/dist/data/designs/ios-apple-maps.md +447 -0
  66. package/dist/data/designs/ios-apple-music-android.md +737 -0
  67. package/dist/data/designs/ios-apple-music-expo.md +596 -0
  68. package/dist/data/designs/ios-apple-music-swiftui.md +578 -0
  69. package/dist/data/designs/ios-apple-music.md +494 -0
  70. package/dist/data/designs/ios-apple-notes-android.md +675 -0
  71. package/dist/data/designs/ios-apple-notes-expo.md +468 -0
  72. package/dist/data/designs/ios-apple-notes-swiftui.md +489 -0
  73. package/dist/data/designs/ios-apple-notes.md +413 -0
  74. package/dist/data/designs/ios-apple-tv-android.md +405 -0
  75. package/dist/data/designs/ios-apple-tv-expo.md +341 -0
  76. package/dist/data/designs/ios-apple-tv-swiftui.md +348 -0
  77. package/dist/data/designs/ios-apple-tv.md +390 -0
  78. package/dist/data/designs/ios-apple-wallet-android.md +617 -0
  79. package/dist/data/designs/ios-apple-wallet-expo.md +487 -0
  80. package/dist/data/designs/ios-apple-wallet-swiftui.md +425 -0
  81. package/dist/data/designs/ios-apple-wallet.md +360 -0
  82. package/dist/data/designs/ios-asana-android.md +435 -0
  83. package/dist/data/designs/ios-asana-expo.md +411 -0
  84. package/dist/data/designs/ios-asana-swiftui.md +416 -0
  85. package/dist/data/designs/ios-asana.md +433 -0
  86. package/dist/data/designs/ios-audible-android.md +450 -0
  87. package/dist/data/designs/ios-audible-expo.md +328 -0
  88. package/dist/data/designs/ios-audible-swiftui.md +418 -0
  89. package/dist/data/designs/ios-audible.md +364 -0
  90. package/dist/data/designs/ios-bandcamp-android.md +471 -0
  91. package/dist/data/designs/ios-bandcamp-expo.md +378 -0
  92. package/dist/data/designs/ios-bandcamp-swiftui.md +374 -0
  93. package/dist/data/designs/ios-bandcamp.md +372 -0
  94. package/dist/data/designs/ios-bear-android.md +496 -0
  95. package/dist/data/designs/ios-bear-expo.md +449 -0
  96. package/dist/data/designs/ios-bear-swiftui.md +479 -0
  97. package/dist/data/designs/ios-bear.md +412 -0
  98. package/dist/data/designs/ios-bereal-android.md +392 -0
  99. package/dist/data/designs/ios-bereal-expo.md +299 -0
  100. package/dist/data/designs/ios-bereal-swiftui.md +319 -0
  101. package/dist/data/designs/ios-bereal.md +336 -0
  102. package/dist/data/designs/ios-binance-android.md +481 -0
  103. package/dist/data/designs/ios-binance-expo.md +404 -0
  104. package/dist/data/designs/ios-binance-swiftui.md +418 -0
  105. package/dist/data/designs/ios-binance.md +410 -0
  106. package/dist/data/designs/ios-bluesky-android.md +442 -0
  107. package/dist/data/designs/ios-bluesky-expo.md +362 -0
  108. package/dist/data/designs/ios-bluesky-swiftui.md +328 -0
  109. package/dist/data/designs/ios-bluesky.md +338 -0
  110. package/dist/data/designs/ios-booking-android.md +471 -0
  111. package/dist/data/designs/ios-booking-expo.md +364 -0
  112. package/dist/data/designs/ios-booking-swiftui.md +377 -0
  113. package/dist/data/designs/ios-booking.md +346 -0
  114. package/dist/data/designs/ios-bumble-android.md +756 -0
  115. package/dist/data/designs/ios-bumble-expo.md +629 -0
  116. package/dist/data/designs/ios-bumble-swiftui.md +546 -0
  117. package/dist/data/designs/ios-bumble.md +424 -0
  118. package/dist/data/designs/ios-cal-ai-android.md +664 -0
  119. package/dist/data/designs/ios-cal-ai-expo.md +415 -0
  120. package/dist/data/designs/ios-cal-ai-swiftui.md +486 -0
  121. package/dist/data/designs/ios-cal-ai.md +387 -0
  122. package/dist/data/designs/ios-calm-android.md +431 -0
  123. package/dist/data/designs/ios-calm-expo.md +384 -0
  124. package/dist/data/designs/ios-calm-swiftui.md +327 -0
  125. package/dist/data/designs/ios-calm.md +356 -0
  126. package/dist/data/designs/ios-canva-android.md +469 -0
  127. package/dist/data/designs/ios-canva-expo.md +374 -0
  128. package/dist/data/designs/ios-canva-swiftui.md +422 -0
  129. package/dist/data/designs/ios-canva.md +395 -0
  130. package/dist/data/designs/ios-cash-app-android.md +601 -0
  131. package/dist/data/designs/ios-cash-app-expo.md +453 -0
  132. package/dist/data/designs/ios-cash-app-swiftui.md +463 -0
  133. package/dist/data/designs/ios-cash-app.md +365 -0
  134. package/dist/data/designs/ios-character-ai-android.md +489 -0
  135. package/dist/data/designs/ios-character-ai-expo.md +420 -0
  136. package/dist/data/designs/ios-character-ai-swiftui.md +417 -0
  137. package/dist/data/designs/ios-character-ai.md +392 -0
  138. package/dist/data/designs/ios-charles-schwab-android.md +454 -0
  139. package/dist/data/designs/ios-charles-schwab-expo.md +388 -0
  140. package/dist/data/designs/ios-charles-schwab-swiftui.md +394 -0
  141. package/dist/data/designs/ios-charles-schwab.md +380 -0
  142. package/dist/data/designs/ios-chatgpt-android.md +753 -0
  143. package/dist/data/designs/ios-chatgpt-expo.md +644 -0
  144. package/dist/data/designs/ios-chatgpt-swiftui.md +653 -0
  145. package/dist/data/designs/ios-chatgpt.md +466 -0
  146. package/dist/data/designs/ios-chime-android.md +438 -0
  147. package/dist/data/designs/ios-chime-expo.md +419 -0
  148. package/dist/data/designs/ios-chime-swiftui.md +383 -0
  149. package/dist/data/designs/ios-chime.md +389 -0
  150. package/dist/data/designs/ios-chipotle-android.md +397 -0
  151. package/dist/data/designs/ios-chipotle-expo.md +301 -0
  152. package/dist/data/designs/ios-chipotle-swiftui.md +345 -0
  153. package/dist/data/designs/ios-chipotle.md +340 -0
  154. package/dist/data/designs/ios-citymapper-android.md +466 -0
  155. package/dist/data/designs/ios-citymapper-expo.md +420 -0
  156. package/dist/data/designs/ios-citymapper-swiftui.md +424 -0
  157. package/dist/data/designs/ios-citymapper.md +394 -0
  158. package/dist/data/designs/ios-claude-android.md +625 -0
  159. package/dist/data/designs/ios-claude-expo.md +671 -0
  160. package/dist/data/designs/ios-claude-swiftui.md +649 -0
  161. package/dist/data/designs/ios-claude.md +447 -0
  162. package/dist/data/designs/ios-clickup-android.md +484 -0
  163. package/dist/data/designs/ios-clickup-expo.md +423 -0
  164. package/dist/data/designs/ios-clickup-swiftui.md +416 -0
  165. package/dist/data/designs/ios-clickup.md +431 -0
  166. package/dist/data/designs/ios-clubhouse-android.md +524 -0
  167. package/dist/data/designs/ios-clubhouse-expo.md +425 -0
  168. package/dist/data/designs/ios-clubhouse-swiftui.md +447 -0
  169. package/dist/data/designs/ios-clubhouse.md +406 -0
  170. package/dist/data/designs/ios-coffee-meets-bagel-android.md +461 -0
  171. package/dist/data/designs/ios-coffee-meets-bagel-expo.md +372 -0
  172. package/dist/data/designs/ios-coffee-meets-bagel-swiftui.md +368 -0
  173. package/dist/data/designs/ios-coffee-meets-bagel.md +376 -0
  174. package/dist/data/designs/ios-coinbase-android.md +611 -0
  175. package/dist/data/designs/ios-coinbase-expo.md +500 -0
  176. package/dist/data/designs/ios-coinbase-swiftui.md +490 -0
  177. package/dist/data/designs/ios-coinbase.md +409 -0
  178. package/dist/data/designs/ios-copilot-android.md +625 -0
  179. package/dist/data/designs/ios-copilot-expo.md +423 -0
  180. package/dist/data/designs/ios-copilot-swiftui.md +471 -0
  181. package/dist/data/designs/ios-copilot.md +367 -0
  182. package/dist/data/designs/ios-craft-android.md +459 -0
  183. package/dist/data/designs/ios-craft-expo.md +417 -0
  184. package/dist/data/designs/ios-craft-swiftui.md +449 -0
  185. package/dist/data/designs/ios-craft.md +449 -0
  186. package/dist/data/designs/ios-crunchyroll-android.md +432 -0
  187. package/dist/data/designs/ios-crunchyroll-expo.md +388 -0
  188. package/dist/data/designs/ios-crunchyroll-swiftui.md +417 -0
  189. package/dist/data/designs/ios-crunchyroll.md +388 -0
  190. package/dist/data/designs/ios-crypto-com-android.md +461 -0
  191. package/dist/data/designs/ios-crypto-com-expo.md +404 -0
  192. package/dist/data/designs/ios-crypto-com-swiftui.md +419 -0
  193. package/dist/data/designs/ios-crypto-com.md +410 -0
  194. package/dist/data/designs/ios-deepseek-android.md +471 -0
  195. package/dist/data/designs/ios-deepseek-expo.md +391 -0
  196. package/dist/data/designs/ios-deepseek-swiftui.md +354 -0
  197. package/dist/data/designs/ios-deepseek.md +393 -0
  198. package/dist/data/designs/ios-deezer-android.md +475 -0
  199. package/dist/data/designs/ios-deezer-expo.md +400 -0
  200. package/dist/data/designs/ios-deezer-swiftui.md +430 -0
  201. package/dist/data/designs/ios-deezer.md +388 -0
  202. package/dist/data/designs/ios-deliveroo-android.md +493 -0
  203. package/dist/data/designs/ios-deliveroo-expo.md +424 -0
  204. package/dist/data/designs/ios-deliveroo-swiftui.md +398 -0
  205. package/dist/data/designs/ios-deliveroo.md +420 -0
  206. package/dist/data/designs/ios-delta-android.md +439 -0
  207. package/dist/data/designs/ios-delta-expo.md +381 -0
  208. package/dist/data/designs/ios-delta-swiftui.md +400 -0
  209. package/dist/data/designs/ios-delta.md +397 -0
  210. package/dist/data/designs/ios-discord-android.md +611 -0
  211. package/dist/data/designs/ios-discord-expo.md +618 -0
  212. package/dist/data/designs/ios-discord-swiftui.md +630 -0
  213. package/dist/data/designs/ios-discord.md +451 -0
  214. package/dist/data/designs/ios-disney-plus-android.md +464 -0
  215. package/dist/data/designs/ios-disney-plus-expo.md +358 -0
  216. package/dist/data/designs/ios-disney-plus-swiftui.md +412 -0
  217. package/dist/data/designs/ios-disney-plus.md +355 -0
  218. package/dist/data/designs/ios-dominos-android.md +417 -0
  219. package/dist/data/designs/ios-dominos-expo.md +316 -0
  220. package/dist/data/designs/ios-dominos-swiftui.md +340 -0
  221. package/dist/data/designs/ios-dominos.md +347 -0
  222. package/dist/data/designs/ios-doordash-android.md +527 -0
  223. package/dist/data/designs/ios-doordash-expo.md +323 -0
  224. package/dist/data/designs/ios-doordash-swiftui.md +301 -0
  225. package/dist/data/designs/ios-doordash.md +393 -0
  226. package/dist/data/designs/ios-dropbox-android.md +522 -0
  227. package/dist/data/designs/ios-dropbox-expo.md +393 -0
  228. package/dist/data/designs/ios-dropbox-swiftui.md +400 -0
  229. package/dist/data/designs/ios-dropbox.md +374 -0
  230. package/dist/data/designs/ios-duolingo-android.md +641 -0
  231. package/dist/data/designs/ios-duolingo-expo.md +597 -0
  232. package/dist/data/designs/ios-duolingo-swiftui.md +539 -0
  233. package/dist/data/designs/ios-duolingo.md +409 -0
  234. package/dist/data/designs/ios-ebay-android.md +436 -0
  235. package/dist/data/designs/ios-ebay-expo.md +344 -0
  236. package/dist/data/designs/ios-ebay-swiftui.md +308 -0
  237. package/dist/data/designs/ios-ebay.md +355 -0
  238. package/dist/data/designs/ios-espn-android.md +468 -0
  239. package/dist/data/designs/ios-espn-expo.md +387 -0
  240. package/dist/data/designs/ios-espn-swiftui.md +410 -0
  241. package/dist/data/designs/ios-espn.md +384 -0
  242. package/dist/data/designs/ios-etsy-android.md +439 -0
  243. package/dist/data/designs/ios-etsy-expo.md +351 -0
  244. package/dist/data/designs/ios-etsy-swiftui.md +296 -0
  245. package/dist/data/designs/ios-etsy.md +355 -0
  246. package/dist/data/designs/ios-evernote-android.md +419 -0
  247. package/dist/data/designs/ios-evernote-expo.md +379 -0
  248. package/dist/data/designs/ios-evernote-swiftui.md +380 -0
  249. package/dist/data/designs/ios-evernote.md +416 -0
  250. package/dist/data/designs/ios-expedia-android.md +451 -0
  251. package/dist/data/designs/ios-expedia-expo.md +433 -0
  252. package/dist/data/designs/ios-expedia-swiftui.md +399 -0
  253. package/dist/data/designs/ios-expedia.md +431 -0
  254. package/dist/data/designs/ios-facebook-android.md +628 -0
  255. package/dist/data/designs/ios-facebook-expo.md +471 -0
  256. package/dist/data/designs/ios-facebook-swiftui.md +459 -0
  257. package/dist/data/designs/ios-facebook.md +452 -0
  258. package/dist/data/designs/ios-feeld-android.md +431 -0
  259. package/dist/data/designs/ios-feeld-expo.md +379 -0
  260. package/dist/data/designs/ios-feeld-swiftui.md +401 -0
  261. package/dist/data/designs/ios-feeld.md +378 -0
  262. package/dist/data/designs/ios-fidelity-android.md +426 -0
  263. package/dist/data/designs/ios-fidelity-expo.md +396 -0
  264. package/dist/data/designs/ios-fidelity-swiftui.md +398 -0
  265. package/dist/data/designs/ios-fidelity.md +387 -0
  266. package/dist/data/designs/ios-figma-android.md +578 -0
  267. package/dist/data/designs/ios-figma-expo.md +504 -0
  268. package/dist/data/designs/ios-figma-swiftui.md +542 -0
  269. package/dist/data/designs/ios-figma.md +404 -0
  270. package/dist/data/designs/ios-fitbit-android.md +439 -0
  271. package/dist/data/designs/ios-fitbit-expo.md +389 -0
  272. package/dist/data/designs/ios-fitbit-swiftui.md +380 -0
  273. package/dist/data/designs/ios-fitbit.md +396 -0
  274. package/dist/data/designs/ios-flickr-android.md +506 -0
  275. package/dist/data/designs/ios-flickr-expo.md +434 -0
  276. package/dist/data/designs/ios-flickr-swiftui.md +421 -0
  277. package/dist/data/designs/ios-flickr.md +394 -0
  278. package/dist/data/designs/ios-flighty-android.md +418 -0
  279. package/dist/data/designs/ios-flighty-expo.md +353 -0
  280. package/dist/data/designs/ios-flighty-swiftui.md +337 -0
  281. package/dist/data/designs/ios-flighty.md +357 -0
  282. package/dist/data/designs/ios-flo-android.md +447 -0
  283. package/dist/data/designs/ios-flo-expo.md +363 -0
  284. package/dist/data/designs/ios-flo-swiftui.md +355 -0
  285. package/dist/data/designs/ios-flo.md +351 -0
  286. package/dist/data/designs/ios-garmin-connect-android.md +531 -0
  287. package/dist/data/designs/ios-garmin-connect-expo.md +429 -0
  288. package/dist/data/designs/ios-garmin-connect-swiftui.md +459 -0
  289. package/dist/data/designs/ios-garmin-connect.md +419 -0
  290. package/dist/data/designs/ios-gas-android.md +438 -0
  291. package/dist/data/designs/ios-gas-expo.md +350 -0
  292. package/dist/data/designs/ios-gas-swiftui.md +329 -0
  293. package/dist/data/designs/ios-gas.md +387 -0
  294. package/dist/data/designs/ios-gemini-android.md +568 -0
  295. package/dist/data/designs/ios-gemini-expo.md +416 -0
  296. package/dist/data/designs/ios-gemini-swiftui.md +424 -0
  297. package/dist/data/designs/ios-gemini.md +351 -0
  298. package/dist/data/designs/ios-github-android.md +507 -0
  299. package/dist/data/designs/ios-github-expo.md +486 -0
  300. package/dist/data/designs/ios-github-swiftui.md +459 -0
  301. package/dist/data/designs/ios-github.md +394 -0
  302. package/dist/data/designs/ios-gmail-android.md +548 -0
  303. package/dist/data/designs/ios-gmail-expo.md +457 -0
  304. package/dist/data/designs/ios-gmail-swiftui.md +470 -0
  305. package/dist/data/designs/ios-gmail.md +441 -0
  306. package/dist/data/designs/ios-goodreads-android.md +436 -0
  307. package/dist/data/designs/ios-goodreads-expo.md +407 -0
  308. package/dist/data/designs/ios-goodreads-swiftui.md +378 -0
  309. package/dist/data/designs/ios-goodreads.md +385 -0
  310. package/dist/data/designs/ios-google-calendar-android.md +565 -0
  311. package/dist/data/designs/ios-google-calendar-expo.md +616 -0
  312. package/dist/data/designs/ios-google-calendar-swiftui.md +585 -0
  313. package/dist/data/designs/ios-google-calendar.md +431 -0
  314. package/dist/data/designs/ios-google-maps-android.md +614 -0
  315. package/dist/data/designs/ios-google-maps-expo.md +515 -0
  316. package/dist/data/designs/ios-google-maps-swiftui.md +484 -0
  317. package/dist/data/designs/ios-google-maps.md +451 -0
  318. package/dist/data/designs/ios-grammarly-android.md +449 -0
  319. package/dist/data/designs/ios-grammarly-expo.md +383 -0
  320. package/dist/data/designs/ios-grammarly-swiftui.md +381 -0
  321. package/dist/data/designs/ios-grammarly.md +392 -0
  322. package/dist/data/designs/ios-grindr-android.md +494 -0
  323. package/dist/data/designs/ios-grindr-expo.md +358 -0
  324. package/dist/data/designs/ios-grindr-swiftui.md +357 -0
  325. package/dist/data/designs/ios-grindr.md +356 -0
  326. package/dist/data/designs/ios-grok-android.md +471 -0
  327. package/dist/data/designs/ios-grok-expo.md +388 -0
  328. package/dist/data/designs/ios-grok-swiftui.md +397 -0
  329. package/dist/data/designs/ios-grok.md +357 -0
  330. package/dist/data/designs/ios-groupme-android.md +551 -0
  331. package/dist/data/designs/ios-groupme-expo.md +442 -0
  332. package/dist/data/designs/ios-groupme-swiftui.md +439 -0
  333. package/dist/data/designs/ios-groupme.md +424 -0
  334. package/dist/data/designs/ios-grubhub-android.md +506 -0
  335. package/dist/data/designs/ios-grubhub-expo.md +428 -0
  336. package/dist/data/designs/ios-grubhub-swiftui.md +408 -0
  337. package/dist/data/designs/ios-grubhub.md +427 -0
  338. package/dist/data/designs/ios-happn-android.md +464 -0
  339. package/dist/data/designs/ios-happn-expo.md +411 -0
  340. package/dist/data/designs/ios-happn-swiftui.md +401 -0
  341. package/dist/data/designs/ios-happn.md +400 -0
  342. package/dist/data/designs/ios-headspace-android.md +688 -0
  343. package/dist/data/designs/ios-headspace-expo.md +574 -0
  344. package/dist/data/designs/ios-headspace-swiftui.md +498 -0
  345. package/dist/data/designs/ios-headspace.md +430 -0
  346. package/dist/data/designs/ios-hevy-android.md +473 -0
  347. package/dist/data/designs/ios-hevy-expo.md +405 -0
  348. package/dist/data/designs/ios-hevy-swiftui.md +367 -0
  349. package/dist/data/designs/ios-hevy.md +410 -0
  350. package/dist/data/designs/ios-hinge-android.md +743 -0
  351. package/dist/data/designs/ios-hinge-expo.md +580 -0
  352. package/dist/data/designs/ios-hinge-swiftui.md +512 -0
  353. package/dist/data/designs/ios-hinge.md +405 -0
  354. package/dist/data/designs/ios-hopper-android.md +440 -0
  355. package/dist/data/designs/ios-hopper-expo.md +353 -0
  356. package/dist/data/designs/ios-hopper-swiftui.md +340 -0
  357. package/dist/data/designs/ios-hopper.md +354 -0
  358. package/dist/data/designs/ios-hulu-android.md +435 -0
  359. package/dist/data/designs/ios-hulu-expo.md +331 -0
  360. package/dist/data/designs/ios-hulu-swiftui.md +374 -0
  361. package/dist/data/designs/ios-hulu.md +353 -0
  362. package/dist/data/designs/ios-iheartradio-android.md +481 -0
  363. package/dist/data/designs/ios-iheartradio-expo.md +417 -0
  364. package/dist/data/designs/ios-iheartradio-swiftui.md +423 -0
  365. package/dist/data/designs/ios-iheartradio.md +381 -0
  366. package/dist/data/designs/ios-imessage-android.md +426 -0
  367. package/dist/data/designs/ios-imessage-expo.md +384 -0
  368. package/dist/data/designs/ios-imessage-swiftui.md +378 -0
  369. package/dist/data/designs/ios-imessage.md +394 -0
  370. package/dist/data/designs/ios-instacart-android.md +471 -0
  371. package/dist/data/designs/ios-instacart-expo.md +349 -0
  372. package/dist/data/designs/ios-instacart-swiftui.md +378 -0
  373. package/dist/data/designs/ios-instacart.md +360 -0
  374. package/dist/data/designs/ios-instagram-android.md +646 -0
  375. package/dist/data/designs/ios-instagram-expo.md +310 -0
  376. package/dist/data/designs/ios-instagram-swiftui.md +333 -0
  377. package/dist/data/designs/ios-instagram.md +376 -0
  378. package/dist/data/designs/ios-jira-android.md +432 -0
  379. package/dist/data/designs/ios-jira-expo.md +375 -0
  380. package/dist/data/designs/ios-jira-swiftui.md +433 -0
  381. package/dist/data/designs/ios-jira.md +424 -0
  382. package/dist/data/designs/ios-kakaotalk-android.md +491 -0
  383. package/dist/data/designs/ios-kakaotalk-expo.md +418 -0
  384. package/dist/data/designs/ios-kakaotalk-swiftui.md +406 -0
  385. package/dist/data/designs/ios-kakaotalk.md +414 -0
  386. package/dist/data/designs/ios-kayak-android.md +466 -0
  387. package/dist/data/designs/ios-kayak-expo.md +407 -0
  388. package/dist/data/designs/ios-kayak-swiftui.md +426 -0
  389. package/dist/data/designs/ios-kayak.md +392 -0
  390. package/dist/data/designs/ios-kick-android.md +487 -0
  391. package/dist/data/designs/ios-kick-expo.md +434 -0
  392. package/dist/data/designs/ios-kick-swiftui.md +429 -0
  393. package/dist/data/designs/ios-kick.md +389 -0
  394. package/dist/data/designs/ios-kik-android.md +433 -0
  395. package/dist/data/designs/ios-kik-expo.md +363 -0
  396. package/dist/data/designs/ios-kik-swiftui.md +363 -0
  397. package/dist/data/designs/ios-kik.md +392 -0
  398. package/dist/data/designs/ios-kindle-android.md +439 -0
  399. package/dist/data/designs/ios-kindle-expo.md +356 -0
  400. package/dist/data/designs/ios-kindle-swiftui.md +372 -0
  401. package/dist/data/designs/ios-kindle.md +369 -0
  402. package/dist/data/designs/ios-klarna-android.md +420 -0
  403. package/dist/data/designs/ios-klarna-expo.md +349 -0
  404. package/dist/data/designs/ios-klarna-swiftui.md +360 -0
  405. package/dist/data/designs/ios-klarna.md +356 -0
  406. package/dist/data/designs/ios-lemon8-android.md +492 -0
  407. package/dist/data/designs/ios-lemon8-expo.md +413 -0
  408. package/dist/data/designs/ios-lemon8-swiftui.md +428 -0
  409. package/dist/data/designs/ios-lemon8.md +378 -0
  410. package/dist/data/designs/ios-life360-android.md +474 -0
  411. package/dist/data/designs/ios-life360-expo.md +375 -0
  412. package/dist/data/designs/ios-life360-swiftui.md +390 -0
  413. package/dist/data/designs/ios-life360.md +402 -0
  414. package/dist/data/designs/ios-line-android.md +517 -0
  415. package/dist/data/designs/ios-line-expo.md +402 -0
  416. package/dist/data/designs/ios-line-swiftui.md +361 -0
  417. package/dist/data/designs/ios-line.md +362 -0
  418. package/dist/data/designs/ios-linear-android.md +457 -0
  419. package/dist/data/designs/ios-linear-expo.md +367 -0
  420. package/dist/data/designs/ios-linear-swiftui.md +401 -0
  421. package/dist/data/designs/ios-linear.md +368 -0
  422. package/dist/data/designs/ios-linkedin-android.md +620 -0
  423. package/dist/data/designs/ios-linkedin-expo.md +469 -0
  424. package/dist/data/designs/ios-linkedin-swiftui.md +472 -0
  425. package/dist/data/designs/ios-linkedin.md +444 -0
  426. package/dist/data/designs/ios-locket-android.md +500 -0
  427. package/dist/data/designs/ios-locket-expo.md +375 -0
  428. package/dist/data/designs/ios-locket-swiftui.md +375 -0
  429. package/dist/data/designs/ios-locket.md +381 -0
  430. package/dist/data/designs/ios-lyft-android.md +406 -0
  431. package/dist/data/designs/ios-lyft-expo.md +333 -0
  432. package/dist/data/designs/ios-lyft-swiftui.md +315 -0
  433. package/dist/data/designs/ios-lyft.md +337 -0
  434. package/dist/data/designs/ios-marriott-bonvoy-android.md +488 -0
  435. package/dist/data/designs/ios-marriott-bonvoy-expo.md +412 -0
  436. package/dist/data/designs/ios-marriott-bonvoy-swiftui.md +461 -0
  437. package/dist/data/designs/ios-marriott-bonvoy.md +379 -0
  438. package/dist/data/designs/ios-mastodon-android.md +476 -0
  439. package/dist/data/designs/ios-mastodon-expo.md +379 -0
  440. package/dist/data/designs/ios-mastodon-swiftui.md +349 -0
  441. package/dist/data/designs/ios-mastodon.md +337 -0
  442. package/dist/data/designs/ios-match-android.md +496 -0
  443. package/dist/data/designs/ios-match-expo.md +393 -0
  444. package/dist/data/designs/ios-match-swiftui.md +450 -0
  445. package/dist/data/designs/ios-match.md +382 -0
  446. package/dist/data/designs/ios-max-android.md +454 -0
  447. package/dist/data/designs/ios-max-expo.md +342 -0
  448. package/dist/data/designs/ios-max-swiftui.md +332 -0
  449. package/dist/data/designs/ios-max.md +352 -0
  450. package/dist/data/designs/ios-mcdonalds-android.md +497 -0
  451. package/dist/data/designs/ios-mcdonalds-expo.md +382 -0
  452. package/dist/data/designs/ios-mcdonalds-swiftui.md +394 -0
  453. package/dist/data/designs/ios-mcdonalds.md +410 -0
  454. package/dist/data/designs/ios-messenger-android.md +523 -0
  455. package/dist/data/designs/ios-messenger-expo.md +379 -0
  456. package/dist/data/designs/ios-messenger-swiftui.md +369 -0
  457. package/dist/data/designs/ios-messenger.md +361 -0
  458. package/dist/data/designs/ios-microsoft-teams-android.md +446 -0
  459. package/dist/data/designs/ios-microsoft-teams-expo.md +362 -0
  460. package/dist/data/designs/ios-microsoft-teams-swiftui.md +384 -0
  461. package/dist/data/designs/ios-microsoft-teams.md +359 -0
  462. package/dist/data/designs/ios-midjourney-android.md +503 -0
  463. package/dist/data/designs/ios-midjourney-expo.md +406 -0
  464. package/dist/data/designs/ios-midjourney-swiftui.md +376 -0
  465. package/dist/data/designs/ios-midjourney.md +407 -0
  466. package/dist/data/designs/ios-miro-android.md +475 -0
  467. package/dist/data/designs/ios-miro-expo.md +421 -0
  468. package/dist/data/designs/ios-miro-swiftui.md +407 -0
  469. package/dist/data/designs/ios-miro.md +424 -0
  470. package/dist/data/designs/ios-monday-android.md +426 -0
  471. package/dist/data/designs/ios-monday-expo.md +382 -0
  472. package/dist/data/designs/ios-monday-swiftui.md +386 -0
  473. package/dist/data/designs/ios-monday.md +415 -0
  474. package/dist/data/designs/ios-monzo-android.md +423 -0
  475. package/dist/data/designs/ios-monzo-expo.md +391 -0
  476. package/dist/data/designs/ios-monzo-swiftui.md +371 -0
  477. package/dist/data/designs/ios-monzo.md +382 -0
  478. package/dist/data/designs/ios-myfitnesspal-android.md +631 -0
  479. package/dist/data/designs/ios-myfitnesspal-expo.md +489 -0
  480. package/dist/data/designs/ios-myfitnesspal-swiftui.md +500 -0
  481. package/dist/data/designs/ios-myfitnesspal.md +408 -0
  482. package/dist/data/designs/ios-netflix-android.md +570 -0
  483. package/dist/data/designs/ios-netflix-expo.md +528 -0
  484. package/dist/data/designs/ios-netflix-swiftui.md +543 -0
  485. package/dist/data/designs/ios-netflix.md +476 -0
  486. package/dist/data/designs/ios-nextdoor-android.md +399 -0
  487. package/dist/data/designs/ios-nextdoor-expo.md +317 -0
  488. package/dist/data/designs/ios-nextdoor-swiftui.md +343 -0
  489. package/dist/data/designs/ios-nextdoor.md +332 -0
  490. package/dist/data/designs/ios-nike-run-club-android.md +598 -0
  491. package/dist/data/designs/ios-nike-run-club-expo.md +459 -0
  492. package/dist/data/designs/ios-nike-run-club-swiftui.md +474 -0
  493. package/dist/data/designs/ios-nike-run-club.md +400 -0
  494. package/dist/data/designs/ios-noom-android.md +455 -0
  495. package/dist/data/designs/ios-noom-expo.md +362 -0
  496. package/dist/data/designs/ios-noom-swiftui.md +369 -0
  497. package/dist/data/designs/ios-noom.md +404 -0
  498. package/dist/data/designs/ios-notion-android.md +575 -0
  499. package/dist/data/designs/ios-notion-expo.md +543 -0
  500. package/dist/data/designs/ios-notion-swiftui.md +562 -0
  501. package/dist/data/designs/ios-notion.md +506 -0
  502. package/dist/data/designs/ios-nubank-android.md +447 -0
  503. package/dist/data/designs/ios-nubank-expo.md +391 -0
  504. package/dist/data/designs/ios-nubank-swiftui.md +391 -0
  505. package/dist/data/designs/ios-nubank.md +388 -0
  506. package/dist/data/designs/ios-obsidian-android.md +456 -0
  507. package/dist/data/designs/ios-obsidian-expo.md +401 -0
  508. package/dist/data/designs/ios-obsidian-swiftui.md +412 -0
  509. package/dist/data/designs/ios-obsidian.md +363 -0
  510. package/dist/data/designs/ios-okcupid-android.md +496 -0
  511. package/dist/data/designs/ios-okcupid-expo.md +378 -0
  512. package/dist/data/designs/ios-okcupid-swiftui.md +343 -0
  513. package/dist/data/designs/ios-okcupid.md +364 -0
  514. package/dist/data/designs/ios-opentable-android.md +426 -0
  515. package/dist/data/designs/ios-opentable-expo.md +350 -0
  516. package/dist/data/designs/ios-opentable-swiftui.md +328 -0
  517. package/dist/data/designs/ios-opentable.md +411 -0
  518. package/dist/data/designs/ios-oura-android.md +426 -0
  519. package/dist/data/designs/ios-oura-expo.md +353 -0
  520. package/dist/data/designs/ios-oura-swiftui.md +327 -0
  521. package/dist/data/designs/ios-oura.md +356 -0
  522. package/dist/data/designs/ios-overcast-android.md +508 -0
  523. package/dist/data/designs/ios-overcast-expo.md +420 -0
  524. package/dist/data/designs/ios-overcast-swiftui.md +427 -0
  525. package/dist/data/designs/ios-overcast.md +414 -0
  526. package/dist/data/designs/ios-pandora-android.md +477 -0
  527. package/dist/data/designs/ios-pandora-expo.md +406 -0
  528. package/dist/data/designs/ios-pandora-swiftui.md +413 -0
  529. package/dist/data/designs/ios-pandora.md +366 -0
  530. package/dist/data/designs/ios-panera-android.md +414 -0
  531. package/dist/data/designs/ios-panera-expo.md +352 -0
  532. package/dist/data/designs/ios-panera-swiftui.md +369 -0
  533. package/dist/data/designs/ios-panera.md +412 -0
  534. package/dist/data/designs/ios-paramount-plus-android.md +415 -0
  535. package/dist/data/designs/ios-paramount-plus-expo.md +363 -0
  536. package/dist/data/designs/ios-paramount-plus-swiftui.md +372 -0
  537. package/dist/data/designs/ios-paramount-plus.md +400 -0
  538. package/dist/data/designs/ios-paypal-android.md +535 -0
  539. package/dist/data/designs/ios-paypal-expo.md +429 -0
  540. package/dist/data/designs/ios-paypal-swiftui.md +466 -0
  541. package/dist/data/designs/ios-paypal.md +393 -0
  542. package/dist/data/designs/ios-peacock-android.md +453 -0
  543. package/dist/data/designs/ios-peacock-expo.md +393 -0
  544. package/dist/data/designs/ios-peacock-swiftui.md +420 -0
  545. package/dist/data/designs/ios-peacock.md +411 -0
  546. package/dist/data/designs/ios-peloton-android.md +470 -0
  547. package/dist/data/designs/ios-peloton-expo.md +401 -0
  548. package/dist/data/designs/ios-peloton-swiftui.md +409 -0
  549. package/dist/data/designs/ios-peloton.md +415 -0
  550. package/dist/data/designs/ios-perplexity-android.md +553 -0
  551. package/dist/data/designs/ios-perplexity-expo.md +703 -0
  552. package/dist/data/designs/ios-perplexity-swiftui.md +602 -0
  553. package/dist/data/designs/ios-perplexity.md +453 -0
  554. package/dist/data/designs/ios-pinterest-android.md +551 -0
  555. package/dist/data/designs/ios-pinterest-expo.md +466 -0
  556. package/dist/data/designs/ios-pinterest-swiftui.md +464 -0
  557. package/dist/data/designs/ios-pinterest.md +427 -0
  558. package/dist/data/designs/ios-plenty-of-fish-android.md +462 -0
  559. package/dist/data/designs/ios-plenty-of-fish-expo.md +380 -0
  560. package/dist/data/designs/ios-plenty-of-fish-swiftui.md +429 -0
  561. package/dist/data/designs/ios-plenty-of-fish.md +387 -0
  562. package/dist/data/designs/ios-plex-android.md +403 -0
  563. package/dist/data/designs/ios-plex-expo.md +333 -0
  564. package/dist/data/designs/ios-plex-swiftui.md +349 -0
  565. package/dist/data/designs/ios-plex.md +379 -0
  566. package/dist/data/designs/ios-pluto-tv-android.md +413 -0
  567. package/dist/data/designs/ios-pluto-tv-expo.md +379 -0
  568. package/dist/data/designs/ios-pluto-tv-swiftui.md +411 -0
  569. package/dist/data/designs/ios-pluto-tv.md +362 -0
  570. package/dist/data/designs/ios-pocket-casts-android.md +492 -0
  571. package/dist/data/designs/ios-pocket-casts-expo.md +430 -0
  572. package/dist/data/designs/ios-pocket-casts-swiftui.md +479 -0
  573. package/dist/data/designs/ios-pocket-casts.md +427 -0
  574. package/dist/data/designs/ios-postman-android.md +472 -0
  575. package/dist/data/designs/ios-postman-expo.md +372 -0
  576. package/dist/data/designs/ios-postman-swiftui.md +377 -0
  577. package/dist/data/designs/ios-postman.md +405 -0
  578. package/dist/data/designs/ios-prime-video-android.md +447 -0
  579. package/dist/data/designs/ios-prime-video-expo.md +365 -0
  580. package/dist/data/designs/ios-prime-video-swiftui.md +333 -0
  581. package/dist/data/designs/ios-prime-video.md +351 -0
  582. package/dist/data/designs/ios-quora-android.md +513 -0
  583. package/dist/data/designs/ios-quora-expo.md +362 -0
  584. package/dist/data/designs/ios-quora-swiftui.md +375 -0
  585. package/dist/data/designs/ios-quora.md +359 -0
  586. package/dist/data/designs/ios-raya-android.md +401 -0
  587. package/dist/data/designs/ios-raya-expo.md +366 -0
  588. package/dist/data/designs/ios-raya-swiftui.md +393 -0
  589. package/dist/data/designs/ios-raya.md +366 -0
  590. package/dist/data/designs/ios-reddit-android.md +605 -0
  591. package/dist/data/designs/ios-reddit-expo.md +553 -0
  592. package/dist/data/designs/ios-reddit-swiftui.md +514 -0
  593. package/dist/data/designs/ios-reddit.md +460 -0
  594. package/dist/data/designs/ios-resy-android.md +430 -0
  595. package/dist/data/designs/ios-resy-expo.md +344 -0
  596. package/dist/data/designs/ios-resy-swiftui.md +330 -0
  597. package/dist/data/designs/ios-resy.md +412 -0
  598. package/dist/data/designs/ios-revolut-android.md +433 -0
  599. package/dist/data/designs/ios-revolut-expo.md +379 -0
  600. package/dist/data/designs/ios-revolut-swiftui.md +372 -0
  601. package/dist/data/designs/ios-revolut.md +353 -0
  602. package/dist/data/designs/ios-robinhood-android.md +587 -0
  603. package/dist/data/designs/ios-robinhood-expo.md +416 -0
  604. package/dist/data/designs/ios-robinhood-swiftui.md +431 -0
  605. package/dist/data/designs/ios-robinhood.md +380 -0
  606. package/dist/data/designs/ios-runna-android.md +485 -0
  607. package/dist/data/designs/ios-runna-expo.md +388 -0
  608. package/dist/data/designs/ios-runna-swiftui.md +413 -0
  609. package/dist/data/designs/ios-runna.md +390 -0
  610. package/dist/data/designs/ios-shazam-android.md +436 -0
  611. package/dist/data/designs/ios-shazam-expo.md +348 -0
  612. package/dist/data/designs/ios-shazam-swiftui.md +394 -0
  613. package/dist/data/designs/ios-shazam.md +338 -0
  614. package/dist/data/designs/ios-signal-android.md +462 -0
  615. package/dist/data/designs/ios-signal-expo.md +361 -0
  616. package/dist/data/designs/ios-signal-swiftui.md +350 -0
  617. package/dist/data/designs/ios-signal.md +357 -0
  618. package/dist/data/designs/ios-skype-android.md +398 -0
  619. package/dist/data/designs/ios-skype-expo.md +368 -0
  620. package/dist/data/designs/ios-skype-swiftui.md +369 -0
  621. package/dist/data/designs/ios-skype.md +406 -0
  622. package/dist/data/designs/ios-skyscanner-android.md +451 -0
  623. package/dist/data/designs/ios-skyscanner-expo.md +423 -0
  624. package/dist/data/designs/ios-skyscanner-swiftui.md +419 -0
  625. package/dist/data/designs/ios-skyscanner.md +421 -0
  626. package/dist/data/designs/ios-slack-android.md +695 -0
  627. package/dist/data/designs/ios-slack-expo.md +601 -0
  628. package/dist/data/designs/ios-slack-swiftui.md +594 -0
  629. package/dist/data/designs/ios-slack.md +474 -0
  630. package/dist/data/designs/ios-sleep-cycle-android.md +436 -0
  631. package/dist/data/designs/ios-sleep-cycle-expo.md +380 -0
  632. package/dist/data/designs/ios-sleep-cycle-swiftui.md +409 -0
  633. package/dist/data/designs/ios-sleep-cycle.md +386 -0
  634. package/dist/data/designs/ios-snapchat-android.md +671 -0
  635. package/dist/data/designs/ios-snapchat-expo.md +528 -0
  636. package/dist/data/designs/ios-snapchat-swiftui.md +557 -0
  637. package/dist/data/designs/ios-snapchat.md +478 -0
  638. package/dist/data/designs/ios-sofi-android.md +446 -0
  639. package/dist/data/designs/ios-sofi-expo.md +387 -0
  640. package/dist/data/designs/ios-sofi-swiftui.md +379 -0
  641. package/dist/data/designs/ios-sofi.md +385 -0
  642. package/dist/data/designs/ios-soundcloud-android.md +488 -0
  643. package/dist/data/designs/ios-soundcloud-expo.md +364 -0
  644. package/dist/data/designs/ios-soundcloud-swiftui.md +399 -0
  645. package/dist/data/designs/ios-soundcloud.md +359 -0
  646. package/dist/data/designs/ios-southwest-android.md +446 -0
  647. package/dist/data/designs/ios-southwest-expo.md +389 -0
  648. package/dist/data/designs/ios-southwest-swiftui.md +405 -0
  649. package/dist/data/designs/ios-southwest.md +405 -0
  650. package/dist/data/designs/ios-spotify-android.md +427 -0
  651. package/dist/data/designs/ios-spotify-expo.md +321 -0
  652. package/dist/data/designs/ios-spotify-swiftui.md +312 -0
  653. package/dist/data/designs/ios-spotify.md +366 -0
  654. package/dist/data/designs/ios-starbucks-android.md +622 -0
  655. package/dist/data/designs/ios-starbucks-expo.md +497 -0
  656. package/dist/data/designs/ios-starbucks-swiftui.md +500 -0
  657. package/dist/data/designs/ios-starbucks.md +440 -0
  658. package/dist/data/designs/ios-strava-android.md +656 -0
  659. package/dist/data/designs/ios-strava-expo.md +501 -0
  660. package/dist/data/designs/ios-strava-swiftui.md +465 -0
  661. package/dist/data/designs/ios-strava.md +433 -0
  662. package/dist/data/designs/ios-strong-android.md +479 -0
  663. package/dist/data/designs/ios-strong-expo.md +396 -0
  664. package/dist/data/designs/ios-strong-swiftui.md +363 -0
  665. package/dist/data/designs/ios-strong.md +415 -0
  666. package/dist/data/designs/ios-substack-android.md +440 -0
  667. package/dist/data/designs/ios-substack-expo.md +395 -0
  668. package/dist/data/designs/ios-substack-swiftui.md +371 -0
  669. package/dist/data/designs/ios-substack.md +372 -0
  670. package/dist/data/designs/ios-swiggy-android.md +549 -0
  671. package/dist/data/designs/ios-swiggy-expo.md +443 -0
  672. package/dist/data/designs/ios-swiggy-swiftui.md +471 -0
  673. package/dist/data/designs/ios-swiggy.md +432 -0
  674. package/dist/data/designs/ios-taco-bell-android.md +529 -0
  675. package/dist/data/designs/ios-taco-bell-expo.md +407 -0
  676. package/dist/data/designs/ios-taco-bell-swiftui.md +419 -0
  677. package/dist/data/designs/ios-taco-bell.md +430 -0
  678. package/dist/data/designs/ios-telegram-android.md +676 -0
  679. package/dist/data/designs/ios-telegram-expo.md +594 -0
  680. package/dist/data/designs/ios-telegram-swiftui.md +518 -0
  681. package/dist/data/designs/ios-telegram.md +458 -0
  682. package/dist/data/designs/ios-the-league-android.md +454 -0
  683. package/dist/data/designs/ios-the-league-expo.md +391 -0
  684. package/dist/data/designs/ios-the-league-swiftui.md +409 -0
  685. package/dist/data/designs/ios-the-league.md +368 -0
  686. package/dist/data/designs/ios-things-3-android.md +421 -0
  687. package/dist/data/designs/ios-things-3-expo.md +351 -0
  688. package/dist/data/designs/ios-things-3-swiftui.md +359 -0
  689. package/dist/data/designs/ios-things-3.md +339 -0
  690. package/dist/data/designs/ios-threads-android.md +588 -0
  691. package/dist/data/designs/ios-threads-expo.md +468 -0
  692. package/dist/data/designs/ios-threads-swiftui.md +452 -0
  693. package/dist/data/designs/ios-threads.md +424 -0
  694. package/dist/data/designs/ios-threema-android.md +496 -0
  695. package/dist/data/designs/ios-threema-expo.md +415 -0
  696. package/dist/data/designs/ios-threema-swiftui.md +446 -0
  697. package/dist/data/designs/ios-threema.md +397 -0
  698. package/dist/data/designs/ios-tidal-android.md +398 -0
  699. package/dist/data/designs/ios-tidal-expo.md +323 -0
  700. package/dist/data/designs/ios-tidal-swiftui.md +340 -0
  701. package/dist/data/designs/ios-tidal.md +351 -0
  702. package/dist/data/designs/ios-tiktok-android.md +654 -0
  703. package/dist/data/designs/ios-tiktok-expo.md +578 -0
  704. package/dist/data/designs/ios-tiktok-swiftui.md +595 -0
  705. package/dist/data/designs/ios-tiktok.md +387 -0
  706. package/dist/data/designs/ios-tinder-android.md +681 -0
  707. package/dist/data/designs/ios-tinder-expo.md +512 -0
  708. package/dist/data/designs/ios-tinder-swiftui.md +497 -0
  709. package/dist/data/designs/ios-tinder.md +415 -0
  710. package/dist/data/designs/ios-todoist-android.md +711 -0
  711. package/dist/data/designs/ios-todoist-expo.md +607 -0
  712. package/dist/data/designs/ios-todoist-swiftui.md +573 -0
  713. package/dist/data/designs/ios-todoist.md +418 -0
  714. package/dist/data/designs/ios-trello-android.md +412 -0
  715. package/dist/data/designs/ios-trello-expo.md +332 -0
  716. package/dist/data/designs/ios-trello-swiftui.md +359 -0
  717. package/dist/data/designs/ios-trello.md +344 -0
  718. package/dist/data/designs/ios-tripadvisor-android.md +450 -0
  719. package/dist/data/designs/ios-tripadvisor-expo.md +322 -0
  720. package/dist/data/designs/ios-tripadvisor-swiftui.md +326 -0
  721. package/dist/data/designs/ios-tripadvisor.md +356 -0
  722. package/dist/data/designs/ios-tubi-android.md +453 -0
  723. package/dist/data/designs/ios-tubi-expo.md +419 -0
  724. package/dist/data/designs/ios-tubi-swiftui.md +463 -0
  725. package/dist/data/designs/ios-tubi.md +383 -0
  726. package/dist/data/designs/ios-tumblr-android.md +462 -0
  727. package/dist/data/designs/ios-tumblr-expo.md +374 -0
  728. package/dist/data/designs/ios-tumblr-swiftui.md +380 -0
  729. package/dist/data/designs/ios-tumblr.md +337 -0
  730. package/dist/data/designs/ios-turo-android.md +500 -0
  731. package/dist/data/designs/ios-turo-expo.md +454 -0
  732. package/dist/data/designs/ios-turo-swiftui.md +447 -0
  733. package/dist/data/designs/ios-turo.md +392 -0
  734. package/dist/data/designs/ios-twitch-android.md +412 -0
  735. package/dist/data/designs/ios-twitch-expo.md +342 -0
  736. package/dist/data/designs/ios-twitch-swiftui.md +328 -0
  737. package/dist/data/designs/ios-twitch.md +354 -0
  738. package/dist/data/designs/ios-uber-android.md +637 -0
  739. package/dist/data/designs/ios-uber-eats-android.md +502 -0
  740. package/dist/data/designs/ios-uber-eats-expo.md +407 -0
  741. package/dist/data/designs/ios-uber-eats-swiftui.md +393 -0
  742. package/dist/data/designs/ios-uber-eats.md +353 -0
  743. package/dist/data/designs/ios-uber-expo.md +512 -0
  744. package/dist/data/designs/ios-uber-swiftui.md +483 -0
  745. package/dist/data/designs/ios-uber.md +395 -0
  746. package/dist/data/designs/ios-united-airlines-android.md +494 -0
  747. package/dist/data/designs/ios-united-airlines-expo.md +408 -0
  748. package/dist/data/designs/ios-united-airlines-swiftui.md +453 -0
  749. package/dist/data/designs/ios-united-airlines.md +388 -0
  750. package/dist/data/designs/ios-venmo-android.md +654 -0
  751. package/dist/data/designs/ios-venmo-expo.md +458 -0
  752. package/dist/data/designs/ios-venmo-swiftui.md +469 -0
  753. package/dist/data/designs/ios-venmo.md +436 -0
  754. package/dist/data/designs/ios-vercel-android.md +454 -0
  755. package/dist/data/designs/ios-vercel-expo.md +387 -0
  756. package/dist/data/designs/ios-vercel-swiftui.md +374 -0
  757. package/dist/data/designs/ios-vercel.md +399 -0
  758. package/dist/data/designs/ios-vero-android.md +498 -0
  759. package/dist/data/designs/ios-vero-expo.md +376 -0
  760. package/dist/data/designs/ios-vero-swiftui.md +374 -0
  761. package/dist/data/designs/ios-vero.md +394 -0
  762. package/dist/data/designs/ios-viber-android.md +377 -0
  763. package/dist/data/designs/ios-viber-expo.md +375 -0
  764. package/dist/data/designs/ios-viber-swiftui.md +378 -0
  765. package/dist/data/designs/ios-viber.md +403 -0
  766. package/dist/data/designs/ios-vimeo-android.md +465 -0
  767. package/dist/data/designs/ios-vimeo-expo.md +347 -0
  768. package/dist/data/designs/ios-vimeo-swiftui.md +383 -0
  769. package/dist/data/designs/ios-vimeo.md +403 -0
  770. package/dist/data/designs/ios-vrbo-android.md +488 -0
  771. package/dist/data/designs/ios-vrbo-expo.md +395 -0
  772. package/dist/data/designs/ios-vrbo-swiftui.md +421 -0
  773. package/dist/data/designs/ios-vrbo.md +383 -0
  774. package/dist/data/designs/ios-vsco-android.md +490 -0
  775. package/dist/data/designs/ios-vsco-expo.md +413 -0
  776. package/dist/data/designs/ios-vsco-swiftui.md +449 -0
  777. package/dist/data/designs/ios-vsco.md +412 -0
  778. package/dist/data/designs/ios-walmart-android.md +479 -0
  779. package/dist/data/designs/ios-walmart-expo.md +370 -0
  780. package/dist/data/designs/ios-walmart-swiftui.md +387 -0
  781. package/dist/data/designs/ios-walmart.md +366 -0
  782. package/dist/data/designs/ios-waze-android.md +695 -0
  783. package/dist/data/designs/ios-waze-expo.md +587 -0
  784. package/dist/data/designs/ios-waze-swiftui.md +601 -0
  785. package/dist/data/designs/ios-waze.md +453 -0
  786. package/dist/data/designs/ios-webull-android.md +429 -0
  787. package/dist/data/designs/ios-webull-expo.md +384 -0
  788. package/dist/data/designs/ios-webull-swiftui.md +406 -0
  789. package/dist/data/designs/ios-webull.md +375 -0
  790. package/dist/data/designs/ios-wechat-android.md +545 -0
  791. package/dist/data/designs/ios-wechat-expo.md +398 -0
  792. package/dist/data/designs/ios-wechat-swiftui.md +394 -0
  793. package/dist/data/designs/ios-wechat.md +363 -0
  794. package/dist/data/designs/ios-whatsapp-android.md +568 -0
  795. package/dist/data/designs/ios-whatsapp-expo.md +502 -0
  796. package/dist/data/designs/ios-whatsapp-swiftui.md +483 -0
  797. package/dist/data/designs/ios-whatsapp.md +425 -0
  798. package/dist/data/designs/ios-whoop-android.md +635 -0
  799. package/dist/data/designs/ios-whoop-expo.md +562 -0
  800. package/dist/data/designs/ios-whoop-swiftui.md +553 -0
  801. package/dist/data/designs/ios-whoop.md +419 -0
  802. package/dist/data/designs/ios-wise-android.md +428 -0
  803. package/dist/data/designs/ios-wise-expo.md +357 -0
  804. package/dist/data/designs/ios-wise-swiftui.md +356 -0
  805. package/dist/data/designs/ios-wise.md +361 -0
  806. package/dist/data/designs/ios-x-twitter-android.md +618 -0
  807. package/dist/data/designs/ios-x-twitter-expo.md +442 -0
  808. package/dist/data/designs/ios-x-twitter-swiftui.md +429 -0
  809. package/dist/data/designs/ios-x-twitter.md +432 -0
  810. package/dist/data/designs/ios-yelp-android.md +455 -0
  811. package/dist/data/designs/ios-yelp-expo.md +397 -0
  812. package/dist/data/designs/ios-yelp-swiftui.md +369 -0
  813. package/dist/data/designs/ios-yelp.md +398 -0
  814. package/dist/data/designs/ios-youtube-android.md +668 -0
  815. package/dist/data/designs/ios-youtube-expo.md +435 -0
  816. package/dist/data/designs/ios-youtube-music-android.md +450 -0
  817. package/dist/data/designs/ios-youtube-music-expo.md +404 -0
  818. package/dist/data/designs/ios-youtube-music-swiftui.md +434 -0
  819. package/dist/data/designs/ios-youtube-music.md +381 -0
  820. package/dist/data/designs/ios-youtube-swiftui.md +496 -0
  821. package/dist/data/designs/ios-youtube.md +466 -0
  822. package/dist/data/designs/ios-zalo-android.md +510 -0
  823. package/dist/data/designs/ios-zalo-expo.md +389 -0
  824. package/dist/data/designs/ios-zalo-swiftui.md +431 -0
  825. package/dist/data/designs/ios-zalo.md +388 -0
  826. package/dist/data/designs/ios-zomato-android.md +527 -0
  827. package/dist/data/designs/ios-zomato-expo.md +437 -0
  828. package/dist/data/designs/ios-zomato-swiftui.md +433 -0
  829. package/dist/data/designs/ios-zomato.md +439 -0
  830. package/dist/data/designs/ios-zoom-android.md +423 -0
  831. package/dist/data/designs/ios-zoom-expo.md +355 -0
  832. package/dist/data/designs/ios-zoom-swiftui.md +379 -0
  833. package/dist/data/designs/ios-zoom.md +357 -0
  834. package/dist/data/designs/ios-zwift-android.md +402 -0
  835. package/dist/data/designs/ios-zwift-expo.md +402 -0
  836. package/dist/data/designs/ios-zwift-swiftui.md +378 -0
  837. package/dist/data/designs/ios-zwift.md +411 -0
  838. package/dist/data/designs/kraken.md +125 -0
  839. package/dist/data/designs/lamborghini.md +288 -0
  840. package/dist/data/designs/linear.app.md +548 -0
  841. package/dist/data/designs/lovable.md +298 -0
  842. package/dist/data/designs/mastercard.md +365 -0
  843. package/dist/data/designs/material-clean.md +289 -0
  844. package/dist/data/designs/meta.md +683 -0
  845. package/dist/data/designs/midnight-pro.md +256 -0
  846. package/dist/data/designs/minimal-brutalist.md +245 -0
  847. package/dist/data/designs/minimax.md +746 -0
  848. package/dist/data/designs/mintlify.md +852 -0
  849. package/dist/data/designs/miro.md +825 -0
  850. package/dist/data/designs/mistral.ai.md +773 -0
  851. package/dist/data/designs/mongodb.md +767 -0
  852. package/dist/data/designs/nike.md +575 -0
  853. package/dist/data/designs/nintendo-2001.md +649 -0
  854. package/dist/data/designs/notion.md +821 -0
  855. package/dist/data/designs/nova-mobile-design-md.md +235 -0
  856. package/dist/data/designs/nvidia.md +640 -0
  857. package/dist/data/designs/ollama.md +539 -0
  858. package/dist/data/designs/opencode.ai.md +521 -0
  859. package/dist/data/designs/pinterest.md +597 -0
  860. package/dist/data/designs/playful-color.md +253 -0
  861. package/dist/data/designs/playstation.md +661 -0
  862. package/dist/data/designs/posthog.md +690 -0
  863. package/dist/data/designs/raycast.md +669 -0
  864. package/dist/data/designs/renault.md +589 -0
  865. package/dist/data/designs/replicate.md +616 -0
  866. package/dist/data/designs/resend.md +585 -0
  867. package/dist/data/designs/revolut.md +636 -0
  868. package/dist/data/designs/runwayml.md +244 -0
  869. package/dist/data/designs/sanity.md +357 -0
  870. package/dist/data/designs/sentry.md +551 -0
  871. package/dist/data/designs/shopify.md +516 -0
  872. package/dist/data/designs/slack.md +482 -0
  873. package/dist/data/designs/soft-glass.md +258 -0
  874. package/dist/data/designs/spacex.md +363 -0
  875. package/dist/data/designs/spotify.md +246 -0
  876. package/dist/data/designs/starbucks.md +580 -0
  877. package/dist/data/designs/stripe.md +487 -0
  878. package/dist/data/designs/supabase.md +462 -0
  879. package/dist/data/designs/superhuman.md +448 -0
  880. package/dist/data/designs/tesla.md +286 -0
  881. package/dist/data/designs/theverge.md +339 -0
  882. package/dist/data/designs/together.ai.md +633 -0
  883. package/dist/data/designs/uber.md +636 -0
  884. package/dist/data/designs/vercel.md +736 -0
  885. package/dist/data/designs/vodafone.md +538 -0
  886. package/dist/data/designs/voltagent.md +521 -0
  887. package/dist/data/designs/warp.md +526 -0
  888. package/dist/data/designs/webflow.md +588 -0
  889. package/dist/data/designs/wired.md +497 -0
  890. package/dist/data/designs/wise.md +544 -0
  891. package/dist/data/designs/x.ai.md +465 -0
  892. package/dist/data/designs/zapier.md +537 -0
  893. package/dist/data/index.json +2600 -0
  894. package/dist/httpServer.js +83 -0
  895. package/dist/index.js +13 -0
  896. package/dist/server.js +32 -0
  897. package/dist/services/designStore.js +59 -0
  898. package/dist/services/linter.js +87 -0
  899. package/dist/services/sectionParser.js +154 -0
  900. package/dist/tools/authoring.js +131 -0
  901. package/dist/tools/browse.js +232 -0
  902. package/dist/tools/diff.js +88 -0
  903. package/dist/tools/getDesign.js +216 -0
  904. package/dist/tools/guardrails.js +87 -0
  905. package/dist/tools/lint.js +104 -0
  906. package/dist/types.js +1 -0
  907. package/package.json +65 -0
@@ -0,0 +1,737 @@
1
+ # Apple Music (iOS) — Jetpack Compose Implementation Guide
2
+
3
+ Companion to [DESIGN.md](DESIGN.md) — the framework-neutral spec. This file ports Apple Music's visual language to **Android with Jetpack Compose (Material 3)**: a color token object, a `Typography` set, paste-ready `@Composable`s, `Modifier`s, motion, and haptics.
4
+
5
+ > Why a Compose guide for an iOS-referenced app? The DESIGN.md tokens are platform-neutral. This file keeps the *visual* identity (Apple Music's true-black/true-white canvas, single Apple-Music-Red accent, 12dp softer album corners, album-art-derived Now Playing gradient, time-synced lyrics) while making everything idiomatic Android — `NavigationBar` instead of a UITabBar, AndroidX Palette instead of Core Image `CIAreaAverage`, a translucent `Surface` instead of `.regularMaterial` (Android has no live blur), `sp`/`dp` instead of `pt`. Apple Music supports both light and dark, so we wire a full Material 3 light + dark scheme; dark canvas is true `#000000` (not `#121212`).
6
+
7
+ Assumes Compose BOM `2024.09+`, Kotlin `2.0+`, `minSdk 24`, Material 3, [Coil](https://coil-kt.github.io/coil/) `2.6+` for artwork, and `androidx.palette:palette-ktx` for the Now Playing album-color extraction.
8
+
9
+ ## 1. Color Tokens
10
+
11
+ ```kotlin
12
+ // ui/theme/AppleMusicColors.kt
13
+ import androidx.compose.ui.graphics.Color
14
+
15
+ object AMColors {
16
+ // Brand
17
+ val Red = Color(0xFFFA2D48) // Play, heart liked, shuffle active, scrubber fill, tab
18
+ val Coral = Color(0xFFFC3C44) // warmer marketing variant
19
+ val RedPressed = Color(0xFFD4213B)
20
+
21
+ // Canvas & Surfaces (Light)
22
+ val CanvasLight = Color(0xFFFFFFFF)
23
+ val Surface1Light = Color(0xFFF2F2F7) // grouped list / section cards
24
+ val Surface3Light = Color(0xFFE5E5EA) // pressed rows, input fill
25
+ val DividerLight = Color(0xFFC6C6C8)
26
+
27
+ // Canvas & Surfaces (Dark) — full-contrast true black
28
+ val CanvasDark = Color(0xFF000000) // unusual for media apps — NOT #121212
29
+ val Surface1Dark = Color(0xFF1C1C1E)
30
+ val Surface2Dark = Color(0xFF2C2C2E)
31
+ val Surface3Dark = Color(0xFF3A3A3C)
32
+ val DividerDark = Color(0xFF38383A)
33
+
34
+ // Text base colors (apply alpha for secondary/tertiary tiers)
35
+ val LabelLight = Color(0xFF000000) // primary, light
36
+ val LabelDark = Color(0xFFFFFFFF) // primary, dark
37
+ val SecLight = Color(0xFF3C3C43) // .copy(alpha = 0.60f) = secondary
38
+ val SecDark = Color(0xFFEBEBF5) // .copy(alpha = 0.60f) = secondary (dark)
39
+
40
+ // Badges
41
+ val AtmosGold = Color(0xFFD4A857)
42
+ val LosslessSilver = Color(0xFF8E8E93)
43
+
44
+ // iOS system colors (prefer for links / status, not the brand red)
45
+ val SystemBlue = Color(0xFF007AFF) // inline links (artist in description)
46
+ val SystemGreen = Color(0xFF34C759) // download complete
47
+ val SystemRed = Color(0xFFFF3B30) // playback errors (NOT Apple Music red)
48
+ }
49
+ ```
50
+
51
+ Apple Music supports light and dark equally — wire both Material 3 schemes. `primary` is Apple Music Red; dark `background` is true black.
52
+
53
+ ```kotlin
54
+ // ui/theme/Theme.kt
55
+ import androidx.compose.material3.lightColorScheme
56
+ import androidx.compose.material3.darkColorScheme
57
+ import androidx.compose.material3.MaterialTheme
58
+ import androidx.compose.foundation.isSystemInDarkTheme
59
+
60
+ private val AMLight = lightColorScheme(
61
+ primary = AMColors.Red,
62
+ onPrimary = Color.White,
63
+ background = AMColors.CanvasLight,
64
+ onBackground = AMColors.LabelLight,
65
+ surface = AMColors.CanvasLight,
66
+ onSurface = AMColors.LabelLight,
67
+ surfaceVariant = AMColors.Surface1Light,
68
+ outline = AMColors.DividerLight,
69
+ error = AMColors.SystemRed,
70
+ )
71
+
72
+ private val AMDark = darkColorScheme(
73
+ primary = AMColors.Red,
74
+ onPrimary = Color.White,
75
+ background = AMColors.CanvasDark, // true #000000
76
+ onBackground = AMColors.LabelDark,
77
+ surface = AMColors.Surface1Dark,
78
+ onSurface = AMColors.LabelDark,
79
+ surfaceVariant = AMColors.Surface2Dark,
80
+ outline = AMColors.DividerDark,
81
+ error = AMColors.SystemRed,
82
+ )
83
+
84
+ @Composable
85
+ fun AppleMusicTheme(
86
+ darkTheme: Boolean = isSystemInDarkTheme(),
87
+ content: @Composable () -> Unit,
88
+ ) = MaterialTheme(
89
+ colorScheme = if (darkTheme) AMDark else AMLight,
90
+ typography = AMTypography,
91
+ content = content,
92
+ )
93
+ ```
94
+
95
+ ## 2. Typography
96
+
97
+ Apple Music is 100% SF Pro with automatic Display/Text optical switching. SF Pro is unavailable on Android — use the system font (Roboto) and reserve a bundled `Inter` for a tighter metric match. SF Pro Rounded (Listen Now hero cards) maps to a rounded family if you bundle one; otherwise keep Roboto. Honor font scaling everywhere (Apple Music is a flagship Dynamic Type app).
98
+
99
+ ```kotlin
100
+ // ui/theme/AppleMusicType.kt
101
+ import androidx.compose.material3.Typography
102
+ import androidx.compose.ui.text.TextStyle
103
+ import androidx.compose.ui.text.font.FontFamily
104
+ import androidx.compose.ui.text.font.FontWeight
105
+ import androidx.compose.ui.unit.sp
106
+
107
+ private val SF = FontFamily.Default // swap to FontFamily(Font(R.font.inter_…))
108
+ private val SFRounded = FontFamily.Default // swap to a rounded family for Listen Now heroes
109
+
110
+ // Named ramp — mirrors DESIGN.md §3 exactly (pt → sp 1:1)
111
+ object AMText {
112
+ val LargeTitle = TextStyle(SF, fontWeight = FontWeight.Bold, fontSize = 34.sp, lineHeight = 37.sp, letterSpacing = 0.37.sp)
113
+ val Title1 = TextStyle(SF, fontWeight = FontWeight.Bold, fontSize = 28.sp, lineHeight = 32.sp, letterSpacing = 0.36.sp)
114
+ val HeroListen = TextStyle(SFRounded, fontWeight = FontWeight.Bold, fontSize = 24.sp, lineHeight = 29.sp, letterSpacing = (-0.2).sp)
115
+ val Title2 = TextStyle(SF, fontWeight = FontWeight.Bold, fontSize = 22.sp, lineHeight = 26.sp, letterSpacing = (-0.26).sp)
116
+ val Title3 = TextStyle(SF, fontWeight = FontWeight.SemiBold, fontSize = 20.sp, lineHeight = 24.sp, letterSpacing = (-0.15).sp)
117
+ val NowPlaying = TextStyle(SF, fontWeight = FontWeight.SemiBold, fontSize = 18.sp, lineHeight = 23.sp, letterSpacing = (-0.1).sp)
118
+ val Headline = TextStyle(SF, fontWeight = FontWeight.SemiBold, fontSize = 17.sp, lineHeight = 21.sp, letterSpacing = (-0.43).sp)
119
+ val Body = TextStyle(SF, fontWeight = FontWeight.Normal, fontSize = 17.sp, lineHeight = 25.sp, letterSpacing = (-0.43).sp)
120
+ val Callout = TextStyle(SF, fontWeight = FontWeight.Normal, fontSize = 16.sp, lineHeight = 20.sp, letterSpacing = (-0.32).sp)
121
+ val Subheadline = TextStyle(SF, fontWeight = FontWeight.Normal, fontSize = 15.sp, lineHeight = 20.sp, letterSpacing = (-0.24).sp)
122
+ val Footnote = TextStyle(SF, fontWeight = FontWeight.Normal, fontSize = 13.sp, lineHeight = 17.sp, letterSpacing = (-0.08).sp)
123
+ val Caption1 = TextStyle(SF, fontWeight = FontWeight.Normal, fontSize = 12.sp, lineHeight = 16.sp)
124
+ val Caption2 = TextStyle(SF, fontWeight = FontWeight.Normal, fontSize = 11.sp, lineHeight = 13.sp, letterSpacing = 0.07.sp)
125
+ val Button = TextStyle(SF, fontWeight = FontWeight.SemiBold, fontSize = 17.sp, lineHeight = 17.sp, letterSpacing = (-0.43).sp)
126
+ val Tab = TextStyle(SF, fontWeight = FontWeight.Medium, fontSize = 10.sp, lineHeight = 10.sp, letterSpacing = (-0.24).sp)
127
+ val LyricsCurrent = TextStyle(SF, fontWeight = FontWeight.Bold, fontSize = 28.sp, lineHeight = 34.sp, letterSpacing = (-0.3).sp)
128
+ val LyricsOther = TextStyle(SF, fontWeight = FontWeight.SemiBold, fontSize = 22.sp, lineHeight = 29.sp, letterSpacing = (-0.2).sp)
129
+ }
130
+
131
+ // Map onto Material 3 slots so stock components inherit the brand
132
+ val AMTypography = Typography(
133
+ headlineLarge = AMText.LargeTitle,
134
+ headlineMedium = AMText.Title1,
135
+ headlineSmall = AMText.Title3,
136
+ titleMedium = AMText.Headline,
137
+ bodyLarge = AMText.Body,
138
+ bodyMedium = AMText.Subheadline,
139
+ labelLarge = AMText.Button,
140
+ labelSmall = AMText.Tab,
141
+ )
142
+ ```
143
+
144
+ ## 3. Signature Components
145
+
146
+ ### Primary Red Play Button
147
+
148
+ ```kotlin
149
+ import androidx.compose.animation.core.animateFloatAsState
150
+ import androidx.compose.animation.core.spring
151
+ import androidx.compose.foundation.background
152
+ import androidx.compose.foundation.clickable
153
+ import androidx.compose.foundation.interaction.MutableInteractionSource
154
+ import androidx.compose.foundation.interaction.collectIsPressedAsState
155
+ import androidx.compose.foundation.layout.size
156
+ import androidx.compose.foundation.shape.CircleShape
157
+ import androidx.compose.material.icons.Icons
158
+ import androidx.compose.material.icons.filled.Pause
159
+ import androidx.compose.material.icons.filled.PlayArrow
160
+ import androidx.compose.material3.Icon
161
+ import androidx.compose.runtime.*
162
+ import androidx.compose.ui.Alignment
163
+ import androidx.compose.ui.Modifier
164
+ import androidx.compose.ui.draw.clip
165
+ import androidx.compose.ui.draw.scale
166
+ import androidx.compose.ui.hapticfeedback.HapticFeedbackType
167
+ import androidx.compose.ui.platform.LocalHapticFeedback
168
+ import androidx.compose.ui.unit.Dp
169
+ import androidx.compose.ui.unit.dp
170
+
171
+ @Composable
172
+ fun ApplePlayButton(
173
+ isPlaying: Boolean,
174
+ onClick: () -> Unit,
175
+ modifier: Modifier = Modifier,
176
+ size: Dp = 64.dp,
177
+ ) {
178
+ val interaction = remember { MutableInteractionSource() }
179
+ val pressed by interaction.collectIsPressedAsState()
180
+ val scale by animateFloatAsState(
181
+ if (pressed) 0.92f else 1f,
182
+ spring(dampingRatio = 0.75f, stiffness = 600f),
183
+ label = "playScale",
184
+ )
185
+ val haptics = LocalHapticFeedback.current
186
+
187
+ Box(
188
+ modifier = modifier
189
+ .size(size)
190
+ .scale(scale)
191
+ .clip(CircleShape)
192
+ .background(AMColors.Red)
193
+ .clickable(interaction, indication = null) {
194
+ haptics.performHapticFeedback(HapticFeedbackType.TextHandleMove) // ~iOS .soft impact
195
+ onClick()
196
+ },
197
+ contentAlignment = Alignment.Center,
198
+ ) {
199
+ Icon(
200
+ imageVector = if (isPlaying) Icons.Filled.Pause else Icons.Filled.PlayArrow,
201
+ contentDescription = if (isPlaying) "Pause" else "Play",
202
+ tint = Color.White,
203
+ modifier = Modifier.size(size * 0.44f),
204
+ )
205
+ }
206
+ }
207
+ ```
208
+
209
+ ### Pill Button (Play / Shuffle / Join Apple Music)
210
+
211
+ ```kotlin
212
+ import androidx.compose.foundation.layout.*
213
+ import androidx.compose.foundation.shape.RoundedCornerShape
214
+ import androidx.compose.material3.Text
215
+ import androidx.compose.ui.graphics.vector.ImageVector
216
+
217
+ @Composable
218
+ fun AMPillButton(
219
+ text: String,
220
+ icon: ImageVector,
221
+ onClick: () -> Unit,
222
+ modifier: Modifier = Modifier,
223
+ tint: Color = AMColors.Red,
224
+ ) {
225
+ val interaction = remember { MutableInteractionSource() }
226
+ val pressed by interaction.collectIsPressedAsState()
227
+ val scale by animateFloatAsState(if (pressed) 0.97f else 1f, label = "pillScale")
228
+ val haptics = LocalHapticFeedback.current
229
+
230
+ Row(
231
+ modifier = modifier
232
+ .scale(scale)
233
+ .clip(RoundedCornerShape(8.dp)) // soft rectangle, NOT a full pill — iOS Music signature
234
+ .background(if (pressed) AMColors.RedPressed else tint)
235
+ .clickable(interaction, indication = null) {
236
+ haptics.performHapticFeedback(HapticFeedbackType.TextHandleMove)
237
+ onClick()
238
+ }
239
+ .padding(horizontal = 24.dp, vertical = 10.dp),
240
+ verticalAlignment = Alignment.CenterVertically,
241
+ horizontalArrangement = Arrangement.spacedBy(8.dp),
242
+ ) {
243
+ Icon(icon, contentDescription = null, tint = Color.White, modifier = Modifier.size(15.dp))
244
+ Text(text, style = AMText.Button, color = Color.White)
245
+ }
246
+ }
247
+ ```
248
+
249
+ ### Album Tile (signature 12dp corner)
250
+
251
+ ```kotlin
252
+ import androidx.compose.ui.draw.shadow
253
+ import androidx.compose.ui.layout.ContentScale
254
+ import androidx.compose.ui.text.style.TextOverflow
255
+ import coil.compose.AsyncImage
256
+
257
+ @Composable
258
+ fun AlbumTile(
259
+ artworkUrl: String,
260
+ title: String,
261
+ subtitle: String,
262
+ onClick: () -> Unit,
263
+ modifier: Modifier = Modifier,
264
+ width: Dp = 160.dp,
265
+ isDark: Boolean = false,
266
+ ) {
267
+ val haptics = LocalHapticFeedback.current
268
+ val interaction = remember { MutableInteractionSource() }
269
+ val pressed by interaction.collectIsPressedAsState()
270
+ val scale by animateFloatAsState(if (pressed) 0.96f else 1f, label = "tileScale")
271
+
272
+ Column(
273
+ modifier = modifier
274
+ .width(width)
275
+ .scale(scale)
276
+ .clickable(interaction, indication = null) {
277
+ haptics.performHapticFeedback(HapticFeedbackType.TextHandleMove) // ~iOS .selection
278
+ onClick()
279
+ },
280
+ verticalArrangement = Arrangement.spacedBy(6.dp),
281
+ ) {
282
+ AsyncImage(
283
+ model = artworkUrl,
284
+ contentDescription = "$title album art",
285
+ modifier = Modifier
286
+ .size(width)
287
+ // generous shadow in light only — dark mode uses surface steps, no shadow
288
+ .then(if (!isDark)
289
+ Modifier.shadow(16.dp, RoundedCornerShape(12.dp),
290
+ spotColor = Color.Black.copy(alpha = 0.08f)) else Modifier)
291
+ .clip(RoundedCornerShape(12.dp)), // signature softer-than-Spotify corner
292
+ contentScale = ContentScale.Crop,
293
+ )
294
+ Text(
295
+ title,
296
+ style = AMText.Subheadline.copy(fontWeight = FontWeight.SemiBold, fontSize = 15.sp),
297
+ color = if (isDark) AMColors.LabelDark else AMColors.LabelLight,
298
+ maxLines = 2, overflow = TextOverflow.Ellipsis,
299
+ )
300
+ Text(
301
+ subtitle,
302
+ style = AMText.Footnote,
303
+ color = (if (isDark) AMColors.SecDark else AMColors.SecLight).copy(alpha = 0.60f),
304
+ maxLines = 1, overflow = TextOverflow.Ellipsis,
305
+ )
306
+ }
307
+ }
308
+ ```
309
+
310
+ ### Track Row (with red equalizer + Atmos badge)
311
+
312
+ ```kotlin
313
+ import androidx.compose.material.icons.filled.MoreHoriz
314
+
315
+ @Composable
316
+ fun TrackRow(
317
+ title: String,
318
+ artist: String,
319
+ artworkUrl: String,
320
+ isPlaying: Boolean,
321
+ hasAtmos: Boolean,
322
+ explicit: Boolean,
323
+ onClick: () -> Unit,
324
+ modifier: Modifier = Modifier,
325
+ ) {
326
+ val interaction = remember { MutableInteractionSource() }
327
+ val pressed by interaction.collectIsPressedAsState()
328
+
329
+ Row(
330
+ modifier = modifier
331
+ .fillMaxWidth()
332
+ .height(56.dp)
333
+ .background(if (pressed) Color.Black.copy(alpha = 0.05f) else Color.Transparent)
334
+ .clickable(interaction, indication = null, onClick = onClick)
335
+ .padding(horizontal = 16.dp),
336
+ verticalAlignment = Alignment.CenterVertically,
337
+ horizontalArrangement = Arrangement.spacedBy(12.dp),
338
+ ) {
339
+ Box {
340
+ AsyncImage(
341
+ model = artworkUrl,
342
+ contentDescription = null,
343
+ modifier = Modifier.size(44.dp).clip(RoundedCornerShape(8.dp)),
344
+ contentScale = ContentScale.Crop,
345
+ )
346
+ if (isPlaying) {
347
+ Box(
348
+ Modifier
349
+ .size(18.dp)
350
+ .align(Alignment.Center)
351
+ .clip(RoundedCornerShape(4.dp))
352
+ .background(Color.Black.copy(alpha = 0.5f)),
353
+ contentAlignment = Alignment.Center,
354
+ ) { Equalizer(color = AMColors.Red) } // red, not Spotify green
355
+ }
356
+ }
357
+ Column(Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(2.dp)) {
358
+ Row(verticalAlignment = Alignment.CenterVertically,
359
+ horizontalArrangement = Arrangement.spacedBy(4.dp)) {
360
+ if (explicit) {
361
+ Box(
362
+ Modifier
363
+ .clip(RoundedCornerShape(3.dp))
364
+ .background(Color.Gray)
365
+ .padding(horizontal = 4.dp),
366
+ ) { Text("E", style = AMText.Caption2.copy(fontWeight = FontWeight.Bold),
367
+ color = Color.White) }
368
+ }
369
+ Text(title, style = AMText.Body.copy(fontSize = 17.sp),
370
+ color = AMColors.LabelLight, maxLines = 1, overflow = TextOverflow.Ellipsis)
371
+ if (hasAtmos) AtmosBadge()
372
+ }
373
+ Text(artist, style = AMText.Subheadline,
374
+ color = AMColors.SecLight.copy(alpha = 0.60f),
375
+ maxLines = 1, overflow = TextOverflow.Ellipsis)
376
+ }
377
+ Icon(Icons.Filled.MoreHoriz, contentDescription = "More",
378
+ tint = AMColors.SecLight.copy(alpha = 0.60f), modifier = Modifier.size(20.dp))
379
+ }
380
+ }
381
+
382
+ @Composable
383
+ fun AtmosBadge() {
384
+ Box(
385
+ Modifier
386
+ .clip(CircleShape) // full pill
387
+ .background(androidx.compose.ui.graphics.Brush.verticalGradient(
388
+ listOf(AMColors.AtmosGold, AMColors.AtmosGold.copy(alpha = 0.85f))))
389
+ .padding(horizontal = 8.dp, vertical = 4.dp),
390
+ ) {
391
+ Text("Dolby Atmos",
392
+ style = AMText.Caption2.copy(fontWeight = FontWeight.SemiBold), color = Color.White)
393
+ }
394
+ }
395
+ ```
396
+
397
+ ### Now Playing Hero (album-art-extracted gradient)
398
+
399
+ ```kotlin
400
+ import androidx.compose.foundation.layout.fillMaxSize
401
+ import androidx.compose.material.icons.filled.SkipNext
402
+ import androidx.compose.material.icons.filled.SkipPrevious
403
+ import androidx.compose.ui.graphics.Brush
404
+
405
+ @Composable
406
+ fun NowPlayingScreen(
407
+ trackTitle: String,
408
+ artist: String,
409
+ artworkUrl: String,
410
+ dominantColor: Color, // from Palette — see §4
411
+ complementaryColor: Color, // darker derived — see §4
412
+ isPlaying: Boolean,
413
+ progress: Float,
414
+ onPlayPause: () -> Unit,
415
+ ) {
416
+ Box(
417
+ Modifier
418
+ .fillMaxSize()
419
+ .background(Brush.verticalGradient(listOf(dominantColor, complementaryColor))),
420
+ ) {
421
+ Column(
422
+ Modifier.fillMaxSize().padding(horizontal = 24.dp),
423
+ horizontalAlignment = Alignment.CenterHorizontally,
424
+ verticalArrangement = Arrangement.Center,
425
+ ) {
426
+ AsyncImage(
427
+ model = artworkUrl,
428
+ contentDescription = "$trackTitle album art",
429
+ modifier = Modifier
430
+ .size(340.dp)
431
+ .shadow(40.dp, RoundedCornerShape(16.dp),
432
+ spotColor = Color.Black.copy(alpha = 0.3f))
433
+ .clip(RoundedCornerShape(16.dp)),
434
+ contentScale = ContentScale.Crop,
435
+ )
436
+ Spacer(Modifier.height(24.dp))
437
+ Text(trackTitle, style = AMText.NowPlaying, color = Color.White, maxLines = 1)
438
+ Text(artist, style = AMText.Subheadline,
439
+ color = Color.White.copy(alpha = 0.7f), maxLines = 1)
440
+ Spacer(Modifier.height(24.dp))
441
+ AMScrubber(progress = progress)
442
+ Spacer(Modifier.height(24.dp))
443
+ Row(verticalAlignment = Alignment.CenterVertically,
444
+ horizontalArrangement = Arrangement.spacedBy(32.dp)) {
445
+ Icon(Icons.Filled.SkipPrevious, "Previous",
446
+ tint = Color.White, modifier = Modifier.size(32.dp))
447
+ ApplePlayButton(isPlaying = isPlaying, onClick = onPlayPause, size = 64.dp)
448
+ Icon(Icons.Filled.SkipNext, "Next",
449
+ tint = Color.White, modifier = Modifier.size(32.dp))
450
+ }
451
+ }
452
+ }
453
+ }
454
+
455
+ @Composable
456
+ fun AMScrubber(progress: Float) {
457
+ Box(Modifier.fillMaxWidth().height(16.dp), contentAlignment = Alignment.CenterStart) {
458
+ Box(Modifier.fillMaxWidth().height(4.dp).clip(CircleShape)
459
+ .background(Color.White.copy(alpha = 0.2f)))
460
+ Box(Modifier.fillMaxWidth(progress).height(4.dp).clip(CircleShape)
461
+ .background(AMColors.Red)) // Apple Music Red fill
462
+ Box(
463
+ Modifier
464
+ .fillMaxWidth(progress)
465
+ .height(16.dp),
466
+ contentAlignment = Alignment.CenterEnd,
467
+ ) {
468
+ Box(Modifier.size(16.dp).clip(CircleShape).background(Color.White)
469
+ .shadow(2.dp, CircleShape, spotColor = Color.Black.copy(alpha = 0.2f)))
470
+ }
471
+ }
472
+ }
473
+ ```
474
+
475
+ ### Mini-Player (translucent surface — no live blur on Android)
476
+
477
+ ```kotlin
478
+ import androidx.compose.material.icons.filled.SkipNext
479
+ import androidx.compose.material3.Surface
480
+
481
+ @Composable
482
+ fun MiniPlayer(
483
+ trackTitle: String,
484
+ artist: String,
485
+ artworkUrl: String,
486
+ isPlaying: Boolean,
487
+ onPlayPause: () -> Unit,
488
+ onExpand: () -> Unit,
489
+ isDark: Boolean = false,
490
+ ) {
491
+ Surface(
492
+ color = (if (isDark) AMColors.Surface1Dark else AMColors.CanvasLight).copy(alpha = 0.80f),
493
+ tonalElevation = 0.dp,
494
+ modifier = Modifier.fillMaxWidth().height(64.dp).clickable(onClick = onExpand),
495
+ ) {
496
+ Row(
497
+ Modifier.padding(start = 12.dp, end = 12.dp),
498
+ verticalAlignment = Alignment.CenterVertically,
499
+ horizontalArrangement = Arrangement.spacedBy(12.dp),
500
+ ) {
501
+ AsyncImage(
502
+ model = artworkUrl, contentDescription = null,
503
+ modifier = Modifier.size(44.dp).clip(RoundedCornerShape(8.dp)),
504
+ contentScale = ContentScale.Crop,
505
+ )
506
+ Column(Modifier.weight(1f)) {
507
+ Text(trackTitle,
508
+ style = AMText.Subheadline.copy(fontWeight = FontWeight.Medium, fontSize = 15.sp),
509
+ color = if (isDark) AMColors.LabelDark else AMColors.LabelLight, maxLines = 1)
510
+ Text(artist, style = AMText.Footnote,
511
+ color = (if (isDark) AMColors.SecDark else AMColors.SecLight).copy(alpha = 0.60f),
512
+ maxLines = 1)
513
+ }
514
+ Icon(
515
+ if (isPlaying) Icons.Filled.Pause else Icons.Filled.PlayArrow,
516
+ contentDescription = if (isPlaying) "Pause" else "Play",
517
+ tint = if (isDark) AMColors.LabelDark else AMColors.LabelLight,
518
+ modifier = Modifier.size(22.dp).clickable(onClick = onPlayPause),
519
+ )
520
+ Icon(Icons.Filled.SkipNext, contentDescription = "Next",
521
+ tint = if (isDark) AMColors.LabelDark else AMColors.LabelLight,
522
+ modifier = Modifier.size(22.dp))
523
+ }
524
+ }
525
+ }
526
+ ```
527
+
528
+ ## 4. Time-Synced Lyrics + Album-Art Color Extraction
529
+
530
+ Apple Music's defining feature is the **time-synced lyrics view** (current line scales up + brightens, auto-scrolls to playback). iOS uses `ScrollViewReader`; Compose uses `LazyColumn` + `LaunchedEffect(currentTimeMs)` + `animateScrollToItem`. Line size/opacity animate via `animateFloatAsState`.
531
+
532
+ ```kotlin
533
+ import androidx.compose.foundation.lazy.LazyColumn
534
+ import androidx.compose.foundation.lazy.itemsIndexed
535
+ import androidx.compose.foundation.lazy.rememberLazyListState
536
+
537
+ data class LyricLine(val startMs: Long, val endMs: Long, val text: String)
538
+
539
+ @Composable
540
+ fun LyricsView(lines: List<LyricLine>, currentTimeMs: Long, gradient: Brush) {
541
+ val listState = rememberLazyListState()
542
+ val currentIndex = lines.indexOfLast { currentTimeMs >= it.startMs }
543
+
544
+ LaunchedEffect(currentIndex) {
545
+ if (currentIndex >= 0) listState.animateScrollToItem(
546
+ index = currentIndex.coerceAtLeast(0),
547
+ scrollOffset = -300, // keep the active line centered
548
+ )
549
+ }
550
+
551
+ LazyColumn(
552
+ state = listState,
553
+ modifier = Modifier.fillMaxSize().background(gradient),
554
+ contentPadding = PaddingValues(horizontal = 24.dp, vertical = 80.dp),
555
+ verticalArrangement = Arrangement.spacedBy(24.dp),
556
+ ) {
557
+ itemsIndexed(lines) { i, line ->
558
+ val isCurrent = i == currentIndex
559
+ val isPast = i < currentIndex
560
+ val targetAlpha = when {
561
+ isCurrent -> 1f
562
+ isPast -> 0.3f // faded above
563
+ else -> 0.6f // upcoming below
564
+ }
565
+ val alpha by animateFloatAsState(targetAlpha,
566
+ spring(dampingRatio = 0.85f), label = "lyricAlpha")
567
+ Text(
568
+ line.text,
569
+ style = if (isCurrent) AMText.LyricsCurrent else AMText.LyricsOther,
570
+ color = Color.White.copy(alpha = alpha),
571
+ )
572
+ }
573
+ }
574
+ }
575
+ ```
576
+
577
+ Album-art color extraction (the Now Playing / Lyrics gradient): Android's analog to iOS Core Image `CIAreaAverage` is **AndroidX Palette**. Load with Coil (software bitmap), pull a vibrant swatch as the dominant, and derive a darker complementary by scaling RGB — exactly Apple Music's "primary at top, darker complementary at bottom".
578
+
579
+ ```kotlin
580
+ // build.gradle: implementation("androidx.palette:palette-ktx:1.0.0")
581
+ import android.content.Context
582
+ import android.graphics.drawable.BitmapDrawable
583
+ import androidx.palette.graphics.Palette
584
+ import coil.imageLoader
585
+ import coil.request.ImageRequest
586
+
587
+ suspend fun extractGradient(context: Context, url: String): Pair<Color, Color> {
588
+ val request = ImageRequest.Builder(context)
589
+ .data(url)
590
+ .allowHardware(false) // Palette needs a software bitmap
591
+ .build()
592
+ val bitmap = (context.imageLoader.execute(request).drawable as? BitmapDrawable)?.bitmap
593
+ ?: return AMColors.CanvasDark to AMColors.CanvasDark
594
+ val palette = Palette.from(bitmap).generate()
595
+ val rgb = (palette.vibrantSwatch ?: palette.dominantSwatch)?.rgb
596
+ ?: return AMColors.CanvasDark to AMColors.CanvasDark
597
+ val dominant = Color(rgb)
598
+ val complementary = Color( // darker derived variant
599
+ red = dominant.red * 0.4f,
600
+ green = dominant.green * 0.4f,
601
+ blue = dominant.blue * 0.4f,
602
+ )
603
+ return dominant to complementary
604
+ }
605
+ ```
606
+
607
+ Hoist into a `ViewModel` and cache per track so scrubbing never re-computes:
608
+
609
+ ```kotlin
610
+ val gradient by produceState(AMColors.CanvasDark to AMColors.CanvasDark, artworkUrl) {
611
+ value = extractGradient(context, artworkUrl)
612
+ }
613
+ ```
614
+
615
+ The mini-player → Now Playing artwork morph is Compose `SharedTransitionLayout` + `Modifier.sharedElement()` (Compose 1.7+) on the 44dp ↔ 340dp artwork; fall back to `AnimatedVisibility` + `scaleIn` over a 0.35s `spring(dampingRatio = 0.8f)`.
616
+
617
+ ## 5. Bottom Navigation (Tab Bar)
618
+
619
+ Use Material 3 `NavigationBar`. Apple Music's iOS tab bar is `.regularMaterial` blur; Android has no first-class live blur, so use an 80%-opaque canvas surface. **Active tint is Apple Music Red** (distinctive — most apps use the system accent for the tab indicator). Five tabs, always labeled.
620
+
621
+ ```kotlin
622
+ import androidx.compose.material.icons.filled.*
623
+ import androidx.compose.material3.NavigationBar
624
+ import androidx.compose.material3.NavigationBarItem
625
+ import androidx.compose.material3.NavigationBarItemDefaults
626
+
627
+ @Composable
628
+ fun AppleMusicBottomBar(selected: Int, onSelect: (Int) -> Unit, isDark: Boolean = false) {
629
+ NavigationBar(
630
+ containerColor = (if (isDark) AMColors.CanvasDark else AMColors.CanvasLight)
631
+ .copy(alpha = 0.80f),
632
+ tonalElevation = 0.dp,
633
+ ) {
634
+ val items = listOf(
635
+ "Home" to Icons.Filled.Home,
636
+ "New" to Icons.Filled.LibraryMusic,
637
+ "Radio" to Icons.Filled.Podcasts,
638
+ "Library" to Icons.Filled.LibraryBooks,
639
+ "Search" to Icons.Filled.Search,
640
+ )
641
+ items.forEachIndexed { i, (label, icon) ->
642
+ NavigationBarItem(
643
+ selected = selected == i,
644
+ onClick = { onSelect(i) },
645
+ icon = { Icon(icon, contentDescription = label, modifier = Modifier.size(25.dp)) },
646
+ label = { Text(label, style = AMText.Tab) },
647
+ colors = NavigationBarItemDefaults.colors(
648
+ selectedIconColor = AMColors.Red, // Apple Music Red, not system accent
649
+ selectedTextColor = AMColors.Red,
650
+ unselectedIconColor = (if (isDark) AMColors.SecDark else AMColors.SecLight)
651
+ .copy(alpha = 0.60f),
652
+ unselectedTextColor = (if (isDark) AMColors.SecDark else AMColors.SecLight)
653
+ .copy(alpha = 0.60f),
654
+ indicatorColor = Color.Transparent, // no Material pill — Apple Music has none
655
+ ),
656
+ )
657
+ }
658
+ }
659
+ }
660
+ ```
661
+
662
+ The persistent **mini-player** sits directly above this bar — render it in the `Scaffold` `bottomBar` slot stacked above `AppleMusicBottomBar`. The collapsing iOS large-title nav maps to `LargeTopAppBar` + `exitUntilCollapsedScrollBehavior()` (34sp → 17sp on scroll).
663
+
664
+ ## 6. Motion
665
+
666
+ | Moment | Compose recipe |
667
+ |--------|----------------|
668
+ | Play button tap | `animateFloatAsState` 1 → 0.92 `spring(dampingRatio = 0.75f)`; `HapticFeedbackType.TextHandleMove` (~iOS `.soft`) |
669
+ | Track change on Now Playing | `Crossfade` artwork with scale 0.98 → 1 over 400ms |
670
+ | Mini-player → full Now Playing | `SharedTransitionLayout` 44 → 340dp, 0.35s `spring(dampingRatio = 0.8f)`; fallback `scaleIn` |
671
+ | Equalizer bars | `rememberInfiniteTransition` 3 bars' `fillMaxHeight`, frozen when paused |
672
+ | Lyrics scroll | active line `animateFloatAsState` alpha 0.6 → 1, size 22 → 28sp, ~300ms, sync to audio |
673
+ | Heart like | `Animatable` 1 → 1.2 → 1 over 300ms, fill → Red; `HapticFeedbackType.LongPress` (~`.success`) |
674
+ | Shuffle toggle | `animateColorAsState` secondary → Red, 200ms; selection haptic |
675
+ | Long-press tile | scale → 0.96 + haptic; context menu via `DropdownMenu` |
676
+
677
+ ```kotlin
678
+ // Equalizer — red bars on the currently-playing row
679
+ @Composable
680
+ fun Equalizer(color: Color, playing: Boolean = true) {
681
+ val t = rememberInfiniteTransition(label = "eq")
682
+ Row(horizontalArrangement = Arrangement.spacedBy(2.dp)) {
683
+ repeat(3) { i ->
684
+ val h by t.animateFloat(
685
+ 0.3f, 1f,
686
+ infiniteRepeatable(tween(400 + i * 120), RepeatMode.Reverse),
687
+ label = "bar$i",
688
+ )
689
+ Box(
690
+ Modifier
691
+ .width(3.dp)
692
+ .fillMaxHeight(if (playing) h else 0.3f)
693
+ .background(color),
694
+ )
695
+ }
696
+ }
697
+ }
698
+ ```
699
+
700
+ Haptics: prefer `LocalHapticFeedback`. For richer control use `LocalView.current.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)` (API 30+) — map `.soft` (Play) to `VibrationEffect.createOneShot(10, ...)`, `.success` (heart like) to a double-buzz `VibrationEffect.createWaveform(longArrayOf(0, 40, 60, 40), -1)`, and `.selection` (shuffle / tab / chip) to a short tick.
701
+
702
+ ## 7. Icons
703
+
704
+ Apple Music uses SF Symbols exclusively; the closest first-party set is `androidx.compose.material:material-icons-extended`. The explicit `E` badge is a custom rounded-rect composable (shown in §3); ship any non-matching glyph as a vector drawable via `ImageVector.vectorResource(R.drawable.…)`.
705
+
706
+ | Component | SF Symbol (iOS) | Material Icon (Compose) |
707
+ |-----------|-----------------|--------------------------|
708
+ | Play | `play.fill` | `Icons.Filled.PlayArrow` |
709
+ | Pause | `pause.fill` | `Icons.Filled.Pause` |
710
+ | Forward / Previous | `forward.fill` / `backward.fill` | `Icons.Filled.SkipNext` / `SkipPrevious` |
711
+ | Shuffle | `shuffle` | `Icons.Filled.Shuffle` |
712
+ | Repeat | `repeat` / `repeat.1` | `Icons.Filled.Repeat` / `RepeatOne` |
713
+ | Heart | `heart` / `heart.fill` | `Icons.Outlined.FavoriteBorder` / `Icons.Filled.Favorite` |
714
+ | Download | `arrow.down.circle` | `Icons.Filled.DownloadForOffline` |
715
+ | Share | `square.and.arrow.up` | `Icons.Filled.IosShare` |
716
+ | More | `ellipsis` | `Icons.Filled.MoreHoriz` |
717
+ | Search | `magnifyingglass` | `Icons.Filled.Search` |
718
+ | Home (Listen Now) | `house` / `house.fill` | `Icons.Filled.Home` |
719
+ | New (Browse) | `music.note.list` | `Icons.Filled.LibraryMusic` |
720
+ | Radio | `dot.radiowaves.left.and.right` | `Icons.Filled.Podcasts` |
721
+ | Library | `square.stack` | `Icons.Filled.LibraryBooks` |
722
+ | Lyrics | `quote.bubble` | `Icons.Filled.Lyrics` |
723
+ | AirPlay | `airplayaudio` | `Icons.Filled.Cast` |
724
+ | Queue | `list.bullet` | `Icons.AutoMirrored.Filled.QueueMusic` |
725
+ | Chevron | `chevron.right` | `Icons.AutoMirrored.Filled.KeyboardArrowRight` |
726
+ | Explicit | custom 'E' in rounded rect | custom composable (see §3) |
727
+
728
+ ## 8. Minimum SDK & Accessibility Notes
729
+
730
+ - **minSdk 24** (Compose floor is 21; Palette + `SharedTransitionLayout` + modern motion are comfortable at 24). `compileSdk 34+`, Compose BOM `2024.09+`, Kotlin `2.0+`.
731
+ - **Edge-to-edge**: call `enableEdgeToEdge()` in the `Activity`. Light canvas wants dark-content (dark icons) system bars; the true-black dark canvas wants light-content. Now Playing's full-bleed gradient should draw under the system bars. Apply `Scaffold` insets so the mini-player clears the gesture nav.
732
+ - **Font scaling**: Apple Music is a flagship Dynamic Type app — `sp` honors the user's font scale; keep it on track titles, artist names, headers, body, and lyrics (current line scales up to ~36sp). Pin layout-sensitive geometry (the 64dp play glyph, scrubber thumb, mini-player frame, 10sp tab labels) by deriving from `dp` or `CompositionLocalProvider(LocalDensity provides Density(density, fontScale = 1f))`. At the largest scales the mini-player drops text and shows only artwork + play.
733
+ - **TalkBack**: the Play button reads `"Play Blinding Lights by The Weeknd"`; the heart reads `"Add to Favorites"` / `"Remove from Favorites"`. Merge track-row text with `Modifier.semantics(mergeDescendants = true)` and mark the trailing ellipsis as a separate button. Lyrics lines expose their text; the current line can announce via `liveRegion`.
734
+ - **Touch targets**: Material guidance is 48.dp minimum. The 64dp Now Playing play button is well clear; inline play buttons and tab items are 44–49dp — bump the 20dp ellipsis and 22dp mini-player controls to a 48.dp hit box via padding.
735
+ - **Contrast**: pure black on white (and white on true black) is WCAG AAA. Secondary at 60% (`#3C3C43`@60% light / `#EBEBF5`@60% dark) passes AA at ≥14sp Medium — acceptable for artist/metadata, not for primary information at small sizes.
736
+ - **Reduce motion**: when `Settings.Global.ANIMATOR_DURATION_SCALE == 0f`, skip the mini-player spring expansion (use a `Crossfade`), freeze the equalizer, and render the current lyric line plainly without the scale animation.
737
+ - **Dynamic color**: do **not** enable Material You `dynamicColorScheme()` — Apple Music Red `#FA2D48`, the true-black `#000000` dark canvas, and the 12dp album corner are signature, intentionally system-Apple choices that must not recolor to the user's wallpaper. The Now Playing gradient comes from album art (Palette), never from the system accent.