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,668 @@
1
+ # YouTube (iOS) — Jetpack Compose Implementation Guide
2
+
3
+ Companion to [DESIGN.md](DESIGN.md) — the framework-neutral spec. This file ports YouTube's visual language to **Android with Jetpack Compose (Material 3)**: a color token object, a `Typography` set, paste-ready `@Composable`s, the 16:9 video card, the draggable mini-player, the Shorts rail, motion, and haptics.
4
+
5
+ > Why a Compose guide for an iOS-referenced app? The DESIGN.md tokens are platform-neutral — and YouTube is itself a Google product whose Material roots already show. This file keeps the *visual* identity (the 16:9 hard-edge thumbnail as the atomic unit, single-red brand accent reserved for Subscribe + brand marks, the near-black `#0F0F0F` dark canvas, the persistent draggable mini-player) while making everything idiomatic Android — `NavigationBar` instead of a UITabBar, `AnchoredDraggable` for the mini-player, a translucent `Surface` (no live blur), `sp`/`dp` instead of `pt`. Light-first with a near-black dark mode; both are wired below.
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 thumbnails, channel avatars, and Shorts audio discs. No color extraction — YouTube's palette is fixed brand chrome.
8
+
9
+ ## 1. Color Tokens
10
+
11
+ ```kotlin
12
+ // ui/theme/YouTubeColors.kt
13
+ import androidx.compose.ui.graphics.Color
14
+
15
+ object YouTubeColors {
16
+ // Brand
17
+ val Red = Color(0xFFFF0000) // Subscribe + brand marks ONLY
18
+ val RedPressed = Color(0xFFCC0000)
19
+ val RedHover = Color(0xFFE60000)
20
+
21
+ // Light canvas
22
+ val CanvasLight = Color(0xFFFFFFFF)
23
+ val Surface1Light = Color(0xFFF9F9F9)
24
+ val Surface2Light = Color(0xFFF2F2F2)
25
+ val DividerLight = Color(0xFFE5E5E5)
26
+
27
+ // Dark canvas
28
+ val CanvasDark = Color(0xFF0F0F0F) // signature near-black, NOT pure black
29
+ val Surface1Dark = Color(0xFF1F1F1F)
30
+ val Surface2Dark = Color(0xFF272727)
31
+ val Surface3Dark = Color(0xFF3F3F3F)
32
+ val DividerDark = Color(0xFF303030)
33
+
34
+ // Text (light)
35
+ val TextPrimaryLight = Color(0xFF0F0F0F)
36
+ val TextSecondaryLight = Color(0xFF606060)
37
+ val TextTertiaryLight = Color(0xFF909090)
38
+ val TextDisabledLight = Color(0xFFC0C0C0)
39
+
40
+ // Text (dark)
41
+ val TextPrimaryDark = Color(0xFFFFFFFF)
42
+ val TextSecondaryDark = Color(0xFFAAAAAA)
43
+ val TextTertiaryDark = Color(0xFF717171)
44
+
45
+ // Semantic
46
+ val InfoBlue = Color(0xFF3EA6FF) // links, "Show more", send-arrow active
47
+ val WarningYellow = Color(0xFFFFC400)
48
+ val Scrim = Color(0xBF000000) // duration tag bg ~rgba(0,0,0,0.75)
49
+ val ShortsTrack = Color(0x4DFFFFFF) // rgba(255,255,255,0.3)
50
+ }
51
+ ```
52
+
53
+ YouTube is light-first with a near-black dark mode. Provide both schemes; `primary` carries the brand red (used by the Subscribe button only — keep it off generic components). The **active tab tint is achromatic** (`#0F0F0F`/`#FFFFFF`), never red.
54
+
55
+ ```kotlin
56
+ // ui/theme/Theme.kt
57
+ import androidx.compose.material3.darkColorScheme
58
+ import androidx.compose.material3.lightColorScheme
59
+ import androidx.compose.material3.MaterialTheme
60
+ import androidx.compose.runtime.Composable
61
+ import androidx.compose.foundation.isSystemInDarkTheme
62
+
63
+ private val YouTubeLight = lightColorScheme(
64
+ primary = YouTubeColors.Red,
65
+ onPrimary = Color.White,
66
+ background = YouTubeColors.CanvasLight,
67
+ onBackground = YouTubeColors.TextPrimaryLight,
68
+ surface = YouTubeColors.CanvasLight,
69
+ onSurface = YouTubeColors.TextPrimaryLight,
70
+ surfaceVariant = YouTubeColors.Surface2Light,
71
+ outline = YouTubeColors.DividerLight,
72
+ )
73
+
74
+ private val YouTubeDark = darkColorScheme(
75
+ primary = YouTubeColors.Red,
76
+ onPrimary = Color.White,
77
+ background = YouTubeColors.CanvasDark,
78
+ onBackground = YouTubeColors.TextPrimaryDark,
79
+ surface = YouTubeColors.CanvasDark,
80
+ onSurface = YouTubeColors.TextPrimaryDark,
81
+ surfaceVariant = YouTubeColors.Surface2Dark,
82
+ outline = YouTubeColors.DividerDark,
83
+ )
84
+
85
+ @Composable
86
+ fun YouTubeTheme(
87
+ darkTheme: Boolean = isSystemInDarkTheme(),
88
+ content: @Composable () -> Unit,
89
+ ) = MaterialTheme(
90
+ colorScheme = if (darkTheme) YouTubeDark else YouTubeLight,
91
+ typography = YouTubeTypography,
92
+ content = content,
93
+ )
94
+ ```
95
+
96
+ ## 2. Typography
97
+
98
+ YouTube pairs **YouTube Sans** (proprietary display, 2017) with **Roboto** (body). Roboto *is* the Android system font (`FontFamily.Default`) — use it directly for body. Drop YouTube Sans TTFs in `res/font/` for display moments; fall back to Roboto if absent. Keep the two families in their lanes.
99
+
100
+ ```kotlin
101
+ // ui/theme/YouTubeType.kt
102
+ import androidx.compose.material3.Typography
103
+ import androidx.compose.ui.text.TextStyle
104
+ import androidx.compose.ui.text.font.Font
105
+ import androidx.compose.ui.text.font.FontFamily
106
+ import androidx.compose.ui.text.font.FontWeight
107
+ import androidx.compose.ui.unit.sp
108
+
109
+ val YouTubeSans = FontFamily(
110
+ Font(R.font.youtube_sans_medium, FontWeight.Medium),
111
+ Font(R.font.youtube_sans_bold, FontWeight.Bold),
112
+ )
113
+ val Roboto = FontFamily.Default // Roboto is Android's system face — exact body match
114
+
115
+ // Named ramp — mirrors DESIGN.md §3 exactly (pt → sp 1:1)
116
+ object YouTubeText {
117
+ val ScreenTitle = TextStyle(YouTubeSans, fontWeight = FontWeight.Bold, fontSize = 20.sp, lineHeight = 24.sp, letterSpacing = (-0.1).sp)
118
+ val VideoDetailTitle = TextStyle(Roboto, fontWeight = FontWeight.SemiBold, fontSize = 18.sp, lineHeight = 23.sp, letterSpacing = (-0.1).sp)
119
+ val VideoTitle = TextStyle(Roboto, fontWeight = FontWeight.Medium, fontSize = 16.sp, lineHeight = 21.sp) // most common moment, 2-line clamp
120
+ val ChannelName = TextStyle(Roboto, fontWeight = FontWeight.Medium, fontSize = 14.sp, lineHeight = 18.sp)
121
+ val Metadata = TextStyle(Roboto, fontWeight = FontWeight.Normal, fontSize = 13.sp, lineHeight = 17.sp)
122
+ val CommentBody = TextStyle(Roboto, fontWeight = FontWeight.Normal, fontSize = 14.sp, lineHeight = 20.sp)
123
+ val CommentAuthor = TextStyle(Roboto, fontWeight = FontWeight.Medium, fontSize = 13.sp, lineHeight = 17.sp)
124
+ val Body = TextStyle(Roboto, fontWeight = FontWeight.Normal, fontSize = 14.sp, lineHeight = 20.sp)
125
+ val Button = TextStyle(Roboto, fontWeight = FontWeight.Medium, fontSize = 14.sp, lineHeight = 14.sp)
126
+ val Chip = TextStyle(Roboto, fontWeight = FontWeight.Normal, fontSize = 14.sp, lineHeight = 14.sp)
127
+ val TabLabel = TextStyle(Roboto, fontWeight = FontWeight.Medium, fontSize = 10.sp, lineHeight = 10.sp, letterSpacing = 0.1.sp)
128
+ val DurationTag = TextStyle(Roboto, fontWeight = FontWeight.Medium, fontSize = 11.sp, lineHeight = 11.sp)
129
+ val Timestamp = TextStyle(Roboto, fontWeight = FontWeight.Normal, fontSize = 12.sp, lineHeight = 12.sp)
130
+ val LikeCount = TextStyle(Roboto, fontWeight = FontWeight.Medium, fontSize = 13.sp, lineHeight = 13.sp)
131
+ val ShortsCaption = TextStyle(YouTubeSans, fontWeight = FontWeight.Medium, fontSize = 15.sp, lineHeight = 20.sp)
132
+ }
133
+
134
+ // Map onto Material 3 slots so stock components inherit the ramp
135
+ val YouTubeTypography = Typography(
136
+ headlineLarge = YouTubeText.ScreenTitle,
137
+ titleLarge = YouTubeText.VideoDetailTitle,
138
+ titleMedium = YouTubeText.VideoTitle,
139
+ bodyMedium = YouTubeText.Body,
140
+ labelSmall = YouTubeText.TabLabel,
141
+ )
142
+ ```
143
+
144
+ ## 3. Signature Components
145
+
146
+ ### Subscribe Button (the iconic red → gray morph)
147
+
148
+ YouTube's signature button shape is an **18dp rounded rectangle** — never a full pill. On subscribe, the red morphs to gray and a bell appears, with a light haptic.
149
+
150
+ ```kotlin
151
+ import androidx.compose.animation.AnimatedVisibility
152
+ import androidx.compose.animation.animateColorAsState
153
+ import androidx.compose.animation.core.spring
154
+ import androidx.compose.animation.fadeIn
155
+ import androidx.compose.animation.fadeOut
156
+ import androidx.compose.animation.slideInHorizontally
157
+ import androidx.compose.foundation.background
158
+ import androidx.compose.foundation.clickable
159
+ import androidx.compose.foundation.layout.*
160
+ import androidx.compose.foundation.shape.RoundedCornerShape
161
+ import androidx.compose.material.icons.Icons
162
+ import androidx.compose.material.icons.filled.Notifications
163
+ import androidx.compose.material.icons.outlined.NotificationsNone
164
+ import androidx.compose.material3.Icon
165
+ import androidx.compose.material3.Text
166
+ import androidx.compose.runtime.*
167
+ import androidx.compose.ui.Alignment
168
+ import androidx.compose.ui.Modifier
169
+ import androidx.compose.ui.draw.clip
170
+ import androidx.compose.ui.hapticfeedback.HapticFeedbackType
171
+ import androidx.compose.ui.platform.LocalHapticFeedback
172
+ import androidx.compose.ui.unit.dp
173
+
174
+ @Composable
175
+ fun SubscribeButton(
176
+ subscribed: Boolean,
177
+ onToggle: () -> Unit,
178
+ modifier: Modifier = Modifier,
179
+ darkTheme: Boolean = false,
180
+ ) {
181
+ val haptics = LocalHapticFeedback.current
182
+ val subscribedBg = if (darkTheme) YouTubeColors.Surface2Dark else YouTubeColors.Surface2Light
183
+ val subscribedFg = if (darkTheme) YouTubeColors.TextPrimaryDark else YouTubeColors.TextPrimaryLight
184
+ val bg by animateColorAsState(
185
+ targetValue = if (subscribed) subscribedBg else YouTubeColors.Red,
186
+ animationSpec = spring(dampingRatio = 0.8f, stiffness = 700f), // ~200ms morph
187
+ label = "subscribeBg",
188
+ )
189
+ var bellOn by remember { mutableStateOf(false) }
190
+
191
+ Row(modifier, verticalAlignment = Alignment.CenterVertically) {
192
+ Box(
193
+ Modifier
194
+ .clip(RoundedCornerShape(18.dp)) // signature rounded-rect, NOT full pill
195
+ .background(bg)
196
+ .clickable {
197
+ haptics.performHapticFeedback(HapticFeedbackType.LongPress) // light/success
198
+ onToggle()
199
+ }
200
+ .padding(horizontal = 16.dp, vertical = 8.dp),
201
+ contentAlignment = Alignment.Center,
202
+ ) {
203
+ Text(
204
+ if (subscribed) "Subscribed" else "Subscribe",
205
+ style = YouTubeText.Button,
206
+ color = if (subscribed) subscribedFg else Color.White,
207
+ )
208
+ }
209
+ AnimatedVisibility(
210
+ visible = subscribed,
211
+ enter = slideInHorizontally { it } + fadeIn(),
212
+ exit = fadeOut(),
213
+ ) {
214
+ Box(
215
+ Modifier
216
+ .padding(start = 4.dp)
217
+ .clip(RoundedCornerShape(18.dp))
218
+ .background(subscribedBg)
219
+ .clickable { bellOn = !bellOn; haptics.performHapticFeedback(HapticFeedbackType.LongPress) }
220
+ .padding(horizontal = 12.dp, vertical = 8.dp),
221
+ ) {
222
+ Icon(
223
+ if (bellOn) Icons.Filled.Notifications else Icons.Outlined.NotificationsNone,
224
+ contentDescription = if (bellOn) "Notifications on" else "Notifications off",
225
+ tint = subscribedFg,
226
+ modifier = Modifier.size(16.dp),
227
+ )
228
+ }
229
+ }
230
+ }
231
+ }
232
+ ```
233
+
234
+ ### Video Card (16:9 hard-edge thumbnail — the atomic unit)
235
+
236
+ The thumbnail bleeds edge-to-edge with **no corner radius** — the hard edge is load-bearing. The duration tag sits absolute bottom-right; a live video shows a red LIVE pill top-left instead.
237
+
238
+ ```kotlin
239
+ import androidx.compose.foundation.layout.aspectRatio
240
+ import androidx.compose.foundation.shape.CircleShape
241
+ import androidx.compose.material.icons.filled.MoreVert
242
+ import androidx.compose.ui.layout.ContentScale
243
+ import androidx.compose.ui.text.style.TextOverflow
244
+ import coil.compose.AsyncImage
245
+
246
+ @Composable
247
+ fun VideoCard(
248
+ thumbnailUrl: String,
249
+ duration: String, // "12:34"
250
+ isLive: Boolean,
251
+ title: String,
252
+ channelAvatarUrl: String,
253
+ channelName: String,
254
+ viewCount: String, // "1.2M views"
255
+ uploadedAgo: String, // "3 days ago"
256
+ onClick: () -> Unit,
257
+ modifier: Modifier = Modifier,
258
+ ) {
259
+ Column(modifier.fillMaxWidth().clickable(onClick = onClick)) {
260
+ Box {
261
+ AsyncImage(
262
+ model = thumbnailUrl,
263
+ contentDescription = title,
264
+ contentScale = ContentScale.Crop,
265
+ modifier = Modifier
266
+ .fillMaxWidth()
267
+ .aspectRatio(16f / 9f), // NO clip — hard edges are the signature
268
+ )
269
+ if (isLive) {
270
+ Row(
271
+ Modifier
272
+ .padding(6.dp)
273
+ .clip(RoundedCornerShape(4.dp))
274
+ .background(YouTubeColors.Red)
275
+ .padding(horizontal = 6.dp, vertical = 4.dp)
276
+ .align(Alignment.TopStart),
277
+ verticalAlignment = Alignment.CenterVertically,
278
+ horizontalArrangement = Arrangement.spacedBy(4.dp),
279
+ ) {
280
+ Box(Modifier.size(6.dp).clip(CircleShape).background(Color.White))
281
+ Text("LIVE", style = YouTubeText.DurationTag.copy(fontWeight = FontWeight.Bold), color = Color.White)
282
+ }
283
+ } else {
284
+ Text(
285
+ duration,
286
+ style = YouTubeText.DurationTag,
287
+ color = Color.White,
288
+ modifier = Modifier
289
+ .padding(6.dp)
290
+ .align(Alignment.BottomEnd)
291
+ .clip(RoundedCornerShape(4.dp))
292
+ .background(YouTubeColors.Scrim)
293
+ .padding(horizontal = 6.dp, vertical = 4.dp),
294
+ )
295
+ }
296
+ }
297
+ Row(
298
+ Modifier.fillMaxWidth().padding(start = 16.dp, end = 8.dp, top = 12.dp, bottom = 16.dp),
299
+ horizontalArrangement = Arrangement.spacedBy(12.dp),
300
+ ) {
301
+ AsyncImage(
302
+ model = channelAvatarUrl,
303
+ contentDescription = null,
304
+ modifier = Modifier.size(28.dp).clip(CircleShape),
305
+ contentScale = ContentScale.Crop,
306
+ )
307
+ Column(Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(4.dp)) {
308
+ Text(title, style = YouTubeText.VideoTitle, maxLines = 2, overflow = TextOverflow.Ellipsis)
309
+ Text(
310
+ "$channelName · $viewCount · $uploadedAgo",
311
+ style = YouTubeText.Metadata,
312
+ color = YouTubeColors.TextSecondaryLight,
313
+ maxLines = 1,
314
+ overflow = TextOverflow.Ellipsis,
315
+ )
316
+ }
317
+ Icon(Icons.Filled.MoreVert, contentDescription = "More", modifier = Modifier.size(20.dp))
318
+ }
319
+ }
320
+ }
321
+ ```
322
+
323
+ ### Action Pill (Like / Dislike / Share — 18dp rounded rect)
324
+
325
+ ```kotlin
326
+ import androidx.compose.ui.graphics.vector.ImageVector
327
+
328
+ @Composable
329
+ fun ActionPill(
330
+ icon: ImageVector,
331
+ label: String?, // null for Dislike (count hidden per 2021 decision)
332
+ active: Boolean,
333
+ onClick: () -> Unit,
334
+ darkTheme: Boolean = false,
335
+ ) {
336
+ val bg = if (darkTheme) YouTubeColors.Surface2Dark else YouTubeColors.Surface2Light
337
+ val fg = if (darkTheme) YouTubeColors.TextPrimaryDark else YouTubeColors.TextPrimaryLight
338
+ Row(
339
+ Modifier
340
+ .clip(RoundedCornerShape(18.dp))
341
+ .background(bg)
342
+ .clickable(onClick = onClick)
343
+ .padding(horizontal = 12.dp, vertical = 8.dp),
344
+ verticalAlignment = Alignment.CenterVertically,
345
+ horizontalArrangement = Arrangement.spacedBy(6.dp),
346
+ ) {
347
+ Icon(icon, contentDescription = null, tint = fg, modifier = Modifier.size(20.dp))
348
+ if (label != null) Text(label, style = YouTubeText.LikeCount, color = fg)
349
+ }
350
+ }
351
+ ```
352
+
353
+ ### Filter Chip Row (full color inversion when selected)
354
+
355
+ ```kotlin
356
+ import androidx.compose.foundation.lazy.LazyRow
357
+ import androidx.compose.foundation.lazy.items
358
+
359
+ @Composable
360
+ fun FilterChipRow(
361
+ chips: List<String>,
362
+ selected: String,
363
+ onSelect: (String) -> Unit,
364
+ darkTheme: Boolean = false,
365
+ ) {
366
+ val defaultBg = if (darkTheme) YouTubeColors.Surface2Dark else YouTubeColors.Surface2Light
367
+ val defaultFg = if (darkTheme) YouTubeColors.TextPrimaryDark else YouTubeColors.TextPrimaryLight
368
+ val activeBg = if (darkTheme) YouTubeColors.TextPrimaryDark else YouTubeColors.TextPrimaryLight
369
+ val activeFg = if (darkTheme) YouTubeColors.CanvasDark else YouTubeColors.CanvasLight
370
+
371
+ LazyRow(
372
+ contentPadding = PaddingValues(horizontal = 12.dp),
373
+ horizontalArrangement = Arrangement.spacedBy(8.dp),
374
+ ) {
375
+ items(chips) { chip ->
376
+ val isSel = chip == selected
377
+ Box(
378
+ Modifier
379
+ .clip(RoundedCornerShape(percent = 50)) // full pill — chips only
380
+ .background(if (isSel) activeBg else defaultBg)
381
+ .clickable { onSelect(chip) }
382
+ .padding(horizontal = 12.dp, vertical = 8.dp),
383
+ ) {
384
+ Text(chip, style = YouTubeText.Chip, color = if (isSel) activeFg else defaultFg)
385
+ }
386
+ }
387
+ }
388
+ }
389
+ ```
390
+
391
+ ## 4. 16:9 Thumbnails · Mini-Player Drag-Down · Shorts Rail
392
+
393
+ YouTube's three most distinctive systems. The **16:9 thumbnail** (above) is the atomic unit. The **mini-player** is YouTube's signature navigation pattern — the full player collapses into a persistent 72dp bar that survives navigation; drag it down past a threshold to dismiss. The **Shorts rail** is a right-edge action stack over a full-bleed 9:16 video.
394
+
395
+ ```kotlin
396
+ import androidx.compose.foundation.gestures.AnchoredDraggableState
397
+ import androidx.compose.foundation.gestures.DraggableAnchors
398
+ import androidx.compose.foundation.gestures.Orientation
399
+ import androidx.compose.foundation.gestures.anchoredDraggable
400
+ import androidx.compose.material.icons.filled.Close
401
+ import androidx.compose.material.icons.filled.Pause
402
+ import androidx.compose.material.icons.filled.PlayArrow
403
+ import kotlin.math.roundToInt
404
+ import androidx.compose.ui.unit.IntOffset
405
+
406
+ enum class PlayerState { Full, Mini, Dismissed }
407
+
408
+ /**
409
+ * Mini-player: a 72dp bar above the bottom bar that persists across navigation.
410
+ * Drag up → expand to full player (use SharedTransitionLayout on the thumbnail).
411
+ * Drag down past threshold → dismiss (video stops).
412
+ */
413
+ @OptIn(ExperimentalFoundationApi::class)
414
+ @Composable
415
+ fun MiniPlayer(
416
+ thumbnailUrl: String,
417
+ title: String,
418
+ channelName: String,
419
+ isPlaying: Boolean,
420
+ onPlayPause: () -> Unit,
421
+ onExpand: () -> Unit,
422
+ onDismiss: () -> Unit,
423
+ dragState: AnchoredDraggableState<PlayerState>,
424
+ modifier: Modifier = Modifier,
425
+ darkTheme: Boolean = false,
426
+ ) {
427
+ val bg = if (darkTheme) YouTubeColors.Surface1Dark else YouTubeColors.CanvasLight
428
+ val fg = if (darkTheme) YouTubeColors.TextPrimaryDark else YouTubeColors.TextPrimaryLight
429
+
430
+ Row(
431
+ modifier = modifier
432
+ .offset { IntOffset(0, dragState.requireOffset().roundToInt()) }
433
+ .anchoredDraggable(dragState, Orientation.Vertical)
434
+ .fillMaxWidth()
435
+ .height(72.dp)
436
+ .background(bg)
437
+ .clickable(onClick = onExpand),
438
+ verticalAlignment = Alignment.CenterVertically,
439
+ horizontalArrangement = Arrangement.spacedBy(12.dp),
440
+ ) {
441
+ AsyncImage(
442
+ model = thumbnailUrl,
443
+ contentDescription = null,
444
+ contentScale = ContentScale.Crop,
445
+ modifier = Modifier.width(128.dp).height(72.dp), // 16:9, hard edges
446
+ )
447
+ Column(Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(2.dp)) {
448
+ Text(title, style = YouTubeText.ChannelName, color = fg, maxLines = 1, overflow = TextOverflow.Ellipsis)
449
+ Text(channelName, style = YouTubeText.Timestamp, color = YouTubeColors.TextSecondaryLight, maxLines = 1)
450
+ }
451
+ Icon(
452
+ if (isPlaying) Icons.Filled.Pause else Icons.Filled.PlayArrow,
453
+ contentDescription = if (isPlaying) "Pause" else "Play",
454
+ tint = fg,
455
+ modifier = Modifier.size(20.dp).clickable(onClick = onPlayPause),
456
+ )
457
+ Icon(
458
+ Icons.Filled.Close,
459
+ contentDescription = "Close",
460
+ tint = fg,
461
+ modifier = Modifier.padding(end = 8.dp).size(20.dp).clickable(onClick = onDismiss),
462
+ )
463
+ }
464
+ }
465
+ ```
466
+
467
+ ### Shorts Right-Rail (over full-bleed 9:16 video)
468
+
469
+ ```kotlin
470
+ import androidx.compose.material.icons.filled.AddCircle
471
+ import androidx.compose.material.icons.filled.ThumbDown
472
+ import androidx.compose.material.icons.filled.ThumbUp
473
+ import androidx.compose.material.icons.outlined.Comment
474
+ import androidx.compose.material.icons.outlined.Reply
475
+ import androidx.compose.material.icons.outlined.ThumbDownOffAlt
476
+ import androidx.compose.material.icons.outlined.ThumbUpOffAlt
477
+ import androidx.compose.foundation.border
478
+ import androidx.compose.ui.draw.shadow
479
+
480
+ @Composable
481
+ fun ShortsActionRail(
482
+ creatorAvatarUrl: String,
483
+ likeCount: String,
484
+ commentCount: String,
485
+ liked: Boolean,
486
+ onLike: () -> Unit,
487
+ modifier: Modifier = Modifier,
488
+ ) {
489
+ // White glyphs with a soft shadow for legibility over arbitrary video
490
+ @Composable
491
+ fun RailIcon(icon: ImageVector, label: String?, cd: String, onClick: () -> Unit) {
492
+ Column(horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(4.dp)) {
493
+ Icon(
494
+ icon, contentDescription = cd, tint = Color.White,
495
+ modifier = Modifier
496
+ .size(32.dp)
497
+ .shadow(2.dp, CircleShape, ambientColor = Color.Black.copy(alpha = 0.4f))
498
+ .clickable(onClick = onClick),
499
+ )
500
+ if (label != null) Text(label, style = YouTubeText.LikeCount, color = Color.White)
501
+ }
502
+ }
503
+
504
+ Column(
505
+ modifier = modifier,
506
+ verticalArrangement = Arrangement.spacedBy(20.dp),
507
+ horizontalAlignment = Alignment.CenterHorizontally,
508
+ ) {
509
+ Box(contentAlignment = Alignment.BottomEnd) {
510
+ AsyncImage(
511
+ model = creatorAvatarUrl,
512
+ contentDescription = "Channel",
513
+ contentScale = ContentScale.Crop,
514
+ modifier = Modifier.size(44.dp).clip(CircleShape).border(2.dp, Color.White, CircleShape),
515
+ )
516
+ Icon( // Subscribe shows as a red "+" badge on the avatar
517
+ Icons.Filled.AddCircle,
518
+ contentDescription = "Subscribe",
519
+ tint = YouTubeColors.Red,
520
+ modifier = Modifier.size(20.dp).offset(x = 4.dp, y = 4.dp),
521
+ )
522
+ }
523
+ RailIcon(if (liked) Icons.Filled.ThumbUp else Icons.Outlined.ThumbUpOffAlt, likeCount, "Like", onLike)
524
+ RailIcon(Icons.Outlined.ThumbDownOffAlt, null, "Dislike") {} // count hidden
525
+ RailIcon(Icons.Outlined.Comment, commentCount, "Comments") {}
526
+ RailIcon(Icons.Outlined.Reply, null, "Share") {}
527
+ }
528
+ }
529
+ ```
530
+
531
+ The Shorts progress scrubber at the bottom is **white, not red** (kept minimal): a 2dp track of `YouTubeColors.ShortsTrack` with a `Color.White` fill — never the brand red on Shorts.
532
+
533
+ ## 5. Bottom Navigation (Tab Bar)
534
+
535
+ Use Material 3 `NavigationBar` with the five canonical tabs (Home, Shorts, Create, Subscriptions, You). YouTube uses a 1dp top hairline rather than a shadow; Android has no live blur, so use an opaque canvas surface with a top divider. **Active tint is achromatic** (`#0F0F0F`/`#FFFFFF`) — never red. The Create tab opens a modal sheet ("Create a Short", "Upload video", "Go live").
536
+
537
+ ```kotlin
538
+ import androidx.compose.material.icons.filled.AddCircleOutline
539
+ import androidx.compose.material.icons.filled.Home
540
+ import androidx.compose.material.icons.filled.PlayCircleFilled
541
+ import androidx.compose.material.icons.filled.Subscriptions
542
+ import androidx.compose.material.icons.outlined.HomeOutlined
543
+ import androidx.compose.material.icons.outlined.PlayCircleOutline
544
+ import androidx.compose.material.icons.outlined.SubscriptionsOutlined
545
+ import androidx.compose.material.icons.outlined.AccountCircle
546
+ import androidx.compose.material3.HorizontalDivider
547
+ import androidx.compose.material3.NavigationBar
548
+ import androidx.compose.material3.NavigationBarItem
549
+ import androidx.compose.material3.NavigationBarItemDefaults
550
+
551
+ @Composable
552
+ fun YouTubeBottomBar(selected: Int, onSelect: (Int) -> Unit, darkTheme: Boolean = false) {
553
+ val container = if (darkTheme) YouTubeColors.CanvasDark else YouTubeColors.CanvasLight
554
+ val divider = if (darkTheme) YouTubeColors.DividerDark else YouTubeColors.DividerLight
555
+ val active = if (darkTheme) YouTubeColors.TextPrimaryDark else YouTubeColors.TextPrimaryLight
556
+ val inactive = if (darkTheme) YouTubeColors.TextSecondaryDark else YouTubeColors.TextSecondaryLight
557
+
558
+ Column {
559
+ HorizontalDivider(thickness = 1.dp, color = divider) // hairline, not a shadow
560
+ NavigationBar(containerColor = container, tonalElevation = 0.dp) {
561
+ data class Tab(val on: ImageVector, val off: ImageVector, val label: String)
562
+ val tabs = listOf(
563
+ Tab(Icons.Filled.Home, Icons.Outlined.HomeOutlined, "Home"),
564
+ Tab(Icons.Filled.PlayCircleFilled, Icons.Outlined.PlayCircleOutline, "Shorts"),
565
+ Tab(Icons.Filled.AddCircleOutline, Icons.Filled.AddCircleOutline, "Create"),
566
+ Tab(Icons.Filled.Subscriptions, Icons.Outlined.SubscriptionsOutlined, "Subscriptions"),
567
+ Tab(Icons.Outlined.AccountCircle, Icons.Outlined.AccountCircle, "You"),
568
+ )
569
+ tabs.forEachIndexed { i, tab ->
570
+ val isSel = selected == i
571
+ NavigationBarItem(
572
+ selected = isSel,
573
+ onClick = { onSelect(i) },
574
+ icon = { Icon(if (isSel) tab.on else tab.off, contentDescription = tab.label, modifier = Modifier.size(24.dp)) },
575
+ label = { Text(tab.label, style = YouTubeText.TabLabel) },
576
+ alwaysShowLabel = true,
577
+ colors = NavigationBarItemDefaults.colors(
578
+ selectedIconColor = active, // achromatic, NOT red
579
+ selectedTextColor = active,
580
+ unselectedIconColor = inactive,
581
+ unselectedTextColor = inactive,
582
+ indicatorColor = Color.Transparent, // no Material pill — YouTube has none
583
+ ),
584
+ )
585
+ }
586
+ }
587
+ }
588
+ }
589
+ ```
590
+
591
+ The **mini-player** renders in the `Scaffold` `bottomBar` slot stacked directly above `YouTubeBottomBar` while playback is active.
592
+
593
+ ## 6. Motion
594
+
595
+ | Moment | Compose recipe |
596
+ |--------|----------------|
597
+ | Subscribe morph | `animateColorAsState` red ↔ gray with `spring(dampingRatio = 0.8f)` (~200ms); bell `AnimatedVisibility` slide+fade; `HapticFeedbackType.LongPress` |
598
+ | Mini-player ↔ full | `SharedTransitionLayout` + `Modifier.sharedElement()` on the 16:9 thumbnail, `spring(dampingRatio = 0.8f, stiffness = 250f)` |
599
+ | Mini-player dismiss | `AnchoredDraggable` past the dismiss anchor → `PlayerState.Dismissed` |
600
+ | Like tap | `Animatable` scale 1 → 1.15 → 1 over 300ms; `HapticFeedbackType.TextHandleMove` |
601
+ | Scrubber drag | thumb scale 1 → 1.3 while dragging; `HapticFeedbackType.SegmentTick` every 10% |
602
+ | Shorts swipe | `VerticalPager` 60fps, `spring(dampingRatio = 0.85f)` |
603
+ | Double-tap seek | tap-zone `pointerInput`; ±10s with an expanding arc-ripple overlay + "−10"/"+10" label |
604
+ | Filter chip select | `animateColorAsState` 200ms inversion; `HapticFeedbackType.SegmentTick` |
605
+
606
+ ```kotlin
607
+ // Like bounce — thumbs-up scales 1 → 1.15 → 1 with a light haptic
608
+ import androidx.compose.animation.core.Animatable
609
+ import androidx.compose.animation.core.tween
610
+ import androidx.compose.ui.draw.scale
611
+
612
+ @Composable
613
+ fun LikeButton(initiallyLiked: Boolean = false) {
614
+ var liked by remember { mutableStateOf(initiallyLiked) }
615
+ val scale = remember { Animatable(1f) }
616
+ val haptics = LocalHapticFeedback.current
617
+ LaunchedEffect(liked) {
618
+ if (liked) {
619
+ haptics.performHapticFeedback(HapticFeedbackType.TextHandleMove) // ~iOS .impact(.light)
620
+ scale.animateTo(1.15f, spring(dampingRatio = 0.6f, stiffness = 600f))
621
+ scale.animateTo(1f, tween(150))
622
+ }
623
+ }
624
+ Icon(
625
+ imageVector = if (liked) Icons.Filled.ThumbUp else Icons.Outlined.ThumbUpOffAlt,
626
+ contentDescription = "Like",
627
+ modifier = Modifier.size(20.dp).scale(scale.value).clickable { liked = !liked },
628
+ )
629
+ }
630
+ ```
631
+
632
+ Haptics: prefer `LocalHapticFeedback`. `HapticFeedbackType.SegmentTick` ≈ iOS `.selection` for filter chips and scrubber ticks; `HapticFeedbackType.LongPress` ≈ `.impact(.light)`/`.success` for Subscribe. For richer control use `LocalView.current.performHapticFeedback(HapticFeedbackConstants.CONFIRM)` (API 30+) on the Subscribe success, or a `Vibrator` `VibrationEffect.createOneShot(10, 70)`.
633
+
634
+ ## 7. Icons
635
+
636
+ YouTube ships custom glyphs (the logomark, the Shorts/Subscriptions marks); the closest first-party set is `androidx.compose.material:material-icons-extended`. The YouTube logomark (red play-button square) and the bespoke Shorts glyph should ship as vector drawables loaded via `ImageVector.vectorResource(R.drawable.…)` — the logomark's red is brand, never a UI accent.
637
+
638
+ | Purpose | SF Symbol (iOS) | Material Icon (Compose) |
639
+ |---------|-----------------|--------------------------|
640
+ | Play / Pause (player) | `play.fill` / `pause.fill` | `Icons.Filled.PlayArrow` / `Icons.Filled.Pause` |
641
+ | Next / Previous | `forward.end.fill` / `backward.end.fill` | `Icons.Filled.SkipNext` / `Icons.Filled.SkipPrevious` |
642
+ | Like | `hand.thumbsup` / `.fill` | `Icons.Outlined.ThumbUpOffAlt` / `Icons.Filled.ThumbUp` |
643
+ | Dislike | `hand.thumbsdown` | `Icons.Outlined.ThumbDownOffAlt` |
644
+ | Share | `arrowshape.turn.up.right` | `Icons.Outlined.Reply` (mirrored) / `Icons.Filled.Share` |
645
+ | Download | `arrow.down.circle` | `Icons.Outlined.FileDownload` |
646
+ | Save (playlist) | `plus.rectangle.on.folder` | `Icons.Outlined.PlaylistAdd` |
647
+ | Comment | `bubble.right` | `Icons.Outlined.Comment` |
648
+ | Subscribe bell | `bell` / `bell.fill` | `Icons.Outlined.NotificationsNone` / `Icons.Filled.Notifications` |
649
+ | Cast | `airplayvideo` | `Icons.Filled.Cast` |
650
+ | Search | `magnifyingglass` | `Icons.Filled.Search` |
651
+ | Home (tab) | `house` / `.fill` | `Icons.Outlined.HomeOutlined` / `Icons.Filled.Home` |
652
+ | Shorts (tab) | `play.rectangle` / `.fill` | `Icons.Outlined.PlayCircleOutline` (or vector drawable) |
653
+ | Create (tab) | `plus.circle.fill` | `Icons.Filled.AddCircleOutline` |
654
+ | Subscriptions (tab) | `play.square.stack` | `Icons.Filled.Subscriptions` |
655
+ | You (tab) | `person.crop.circle` | `Icons.Outlined.AccountCircle` |
656
+ | More | `ellipsis` | `Icons.Filled.MoreVert` |
657
+ | Close | `xmark` | `Icons.Filled.Close` |
658
+
659
+ ## 8. Minimum SDK & Accessibility Notes
660
+
661
+ - **minSdk 24** (Compose floor is 21; `AnchoredDraggable`, `SharedTransitionLayout` (Compose 1.7+), and `VerticalPager` are comfortable at 24). `compileSdk 34+`, Compose BOM `2024.09+`, Kotlin `2.0+`.
662
+ - **Edge-to-edge**: call `enableEdgeToEdge()` in the `Activity`; system-bar icon color follows the theme. Let Shorts video extend under the system bars while keeping overlays inside `WindowInsets.systemBars`; apply `Scaffold` insets so the mini-player + tab bar clear the gesture nav. Video detail should support landscape fullscreen (let the player ignore insets, controls respect them).
663
+ - **Font scaling**: `sp` honors the user's font scale — keep it on video titles, descriptions, and comments. **Pin** the duration tag (11sp), Subscribe button text (14sp), tab labels (10sp), and Shorts captions by deriving them from a fixed-`fontScale` `LocalDensity` — these are layout-critical (the tag overlays the thumbnail corner; the chip/tab rows must not wrap).
664
+ - **TalkBack**: merge each video card into one node with `Modifier.semantics(mergeDescendants = true)` and a readable label (`"<title>, <channel>, <views>, <age>"`); expose Subscribe and the overflow as separate buttons. Shorts rail icons are independent buttons with state-aware descriptions.
665
+ - **Touch targets**: Material guidance is 48.dp minimum. The Subscribe pill is 36dp tall — expand the hit area to 48dp via padding. The 20dp overflow and 20dp mini-player controls need a 44–48dp hit area; Shorts 32dp glyphs need a 48dp hit area.
666
+ - **Reduce Motion**: respect the system "remove animations" setting — fall back the Subscribe morph to an instant cross-fade, skip the double-tap-seek ripple and the like bounce, and make the mini-player transition a snap.
667
+ - **Contrast**: `#606060` on `#FFFFFF` meets WCAG AA at 13sp+; `#AAAAAA` on `#0F0F0F` passes. Validate the lowest-emphasis tertiary (`#909090`/`#717171`) at the 11–12sp tag/timestamp sizes; Shorts captions over arbitrary video must keep the `rgba(0,0,0,0.5)` text shadow for legibility regardless of frame content.
668
+ - **Dynamic color**: YouTube is a Google product, but its brand red and the achromatic chrome are fixed — do **not** enable Material You `dynamicColorScheme()`; the Subscribe-red and near-black canvas must hold regardless of wallpaper. Keep the hand-built light/dark schemes above. Honor the device mute switch on Shorts autoplay and surface an audio-state indicator.