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,641 @@
1
+ # Duolingo (iOS) — Jetpack Compose Implementation Guide
2
+
3
+ Companion to [DESIGN.md](DESIGN.md) — the framework-neutral spec. This file ports Duolingo'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 (Duolingo's snow-white canvas, saturated branded palette, the 3D "slab" button with its collapsing ledge, the curved learning path, the gamified streak/gem/heart HUD, and Duo the owl as emotion carrier) while making everything idiomatic Android — `NavigationBar` instead of a UITabBar, `FontFamily(Font(R.font.…))` instead of `Font.custom`, `sp`/`dp` instead of `pt`.
6
+
7
+ Assumes Compose BOM `2024.09+`, Kotlin `2.0+`, `minSdk 24`, Material 3, and [Coil](https://coil-kt.github.io/coil/) `2.6+` only if you load remote avatars (Duo illustrations ship as bundled assets — no Palette needed).
8
+
9
+ ## 1. Color Tokens
10
+
11
+ ```kotlin
12
+ // ui/theme/DuolingoColors.kt
13
+ import androidx.compose.ui.graphics.Color
14
+
15
+ object DuolingoColors {
16
+ // Brand greens — the verb
17
+ val FeatherGreen = Color(0xFF58CC02) // primary CTA, correct, completed nodes
18
+ val MaskGreen = Color(0xFF89E219) // progress fills, button top highlight
19
+ val ButtonGreenLedge = Color(0xFF58A700) // the darker 3D ledge
20
+
21
+ // Gamification
22
+ val CardinalRed = Color(0xFFFF4B4B) // wrong, lost heart, destructive
23
+ val CardinalLedge = Color(0xFFE53030)
24
+ val FoxOrange = Color(0xFFFF9600) // streak flame
25
+ val BeeYellow = Color(0xFFFFC800) // gems, rewards
26
+ val MacawBlue = Color(0xFF1CB0F6) // info, links, gem icon
27
+ val BeetlePurple = Color(0xFFCE82FF) // Super Duolingo (premium)
28
+
29
+ // Canvas & surfaces (light)
30
+ val Snow = Color(0xFFFFFFFF)
31
+ val Polar = Color(0xFFF7F7F7)
32
+ val Swan = Color(0xFFE5E5E5) // tile fill, locked node, progress track
33
+ val SwanLedge = Color(0xFFC4C4C4)
34
+
35
+ // Canvas & surfaces (dark — blue-tinted navy, not true black)
36
+ val DarkCanvas = Color(0xFF131F24)
37
+ val DarkSurface1 = Color(0xFF1F2C34)
38
+ val DarkSurface2 = Color(0xFF37464F)
39
+
40
+ // Text
41
+ val Eel = Color(0xFF4B4B4B) // primary — never pure #000
42
+ val Hare = Color(0xFFAFAFAF) // secondary
43
+ val Wolf = Color(0xFF777777) // tertiary
44
+
45
+ // Tile states
46
+ val TileSelectedFill = Color(0xFFDDF4FF)
47
+ val TileSelectedBorder = Color(0xFF84D8FF)
48
+ val TileCorrectFill = Color(0xFFD7FFB8)
49
+ val TileWrongFill = Color(0xFFFFDFE0)
50
+ val GoldLedge = Color(0xFFE29F03) // gold-crowned node ledge
51
+ }
52
+ ```
53
+
54
+ Duolingo is snow-white first; wire a `lightColorScheme` with a blue-tinted-navy `darkColorScheme`. The accent palette keeps its light-mode hex on dark — the brand stays bright against the navy.
55
+
56
+ ```kotlin
57
+ // ui/theme/Theme.kt
58
+ import androidx.compose.material3.MaterialTheme
59
+ import androidx.compose.material3.darkColorScheme
60
+ import androidx.compose.material3.lightColorScheme
61
+ import androidx.compose.runtime.Composable
62
+
63
+ private val DuolingoLight = lightColorScheme(
64
+ primary = DuolingoColors.FeatherGreen,
65
+ onPrimary = DuolingoColors.Snow,
66
+ background = DuolingoColors.Snow,
67
+ onBackground = DuolingoColors.Eel,
68
+ surface = DuolingoColors.Snow,
69
+ onSurface = DuolingoColors.Eel,
70
+ surfaceVariant = DuolingoColors.Polar,
71
+ outline = DuolingoColors.Swan,
72
+ error = DuolingoColors.CardinalRed,
73
+ )
74
+
75
+ private val DuolingoDark = darkColorScheme(
76
+ primary = DuolingoColors.FeatherGreen, // stays bright on navy
77
+ onPrimary = DuolingoColors.Snow,
78
+ background = DuolingoColors.DarkCanvas,
79
+ onBackground = DuolingoColors.Snow,
80
+ surface = DuolingoColors.DarkSurface1,
81
+ onSurface = DuolingoColors.Snow,
82
+ surfaceVariant = DuolingoColors.DarkSurface2,
83
+ outline = DuolingoColors.DarkSurface2,
84
+ error = DuolingoColors.CardinalRed,
85
+ )
86
+
87
+ @Composable
88
+ fun DuolingoTheme(useDark: Boolean = false, content: @Composable () -> Unit) =
89
+ MaterialTheme(
90
+ colorScheme = if (useDark) DuolingoDark else DuolingoLight,
91
+ typography = DuolingoTypography,
92
+ content = content,
93
+ )
94
+ ```
95
+
96
+ ## 2. Typography
97
+
98
+ `Feather Bold` (Johnson Banks / Krista Radoeva, 2019 — headlines + numbers) and `DIN Next Rounded Pro` (Monotype — all UI) are proprietary. Drop the TTFs in `res/font/` (lowercase, snake_case). Fall back to **SF Pro Rounded's Android analog** — the system rounded face (Roboto with `FontVariation`, or bundle a rounded substitute); rounded terminals come closest.
99
+
100
+ ```kotlin
101
+ // ui/theme/DuolingoType.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
+ // Feather Bold — emotion: titles, hero numbers, Duo-said-so moments
110
+ val Feather = FontFamily(Font(R.font.feather_bold, FontWeight.Bold))
111
+
112
+ // DIN Next Rounded Pro — everything else: buttons, tiles, labels
113
+ val DinRounded = FontFamily(
114
+ Font(R.font.din_next_rounded_pro_regular, FontWeight.Normal),
115
+ Font(R.font.din_next_rounded_pro_medium, FontWeight.Medium),
116
+ Font(R.font.din_next_rounded_pro_bold, FontWeight.Bold),
117
+ Font(R.font.din_next_rounded_pro_black, FontWeight.Black), // the "800" button voice
118
+ )
119
+
120
+ // Named ramp — mirrors DESIGN.md §3 exactly (pt → sp 1:1)
121
+ object DuolingoText {
122
+ val Wordmark = TextStyle(Feather, fontWeight = FontWeight.Bold, fontSize = 34.sp, lineHeight = 34.sp, letterSpacing = (-0.5).sp)
123
+ val HeroNumber = TextStyle(Feather, fontWeight = FontWeight.Bold, fontSize = 56.sp, lineHeight = 56.sp, letterSpacing = (-1.0).sp)
124
+ val ScreenTitle = TextStyle(Feather, fontWeight = FontWeight.Bold, fontSize = 28.sp, lineHeight = 32.sp, letterSpacing = (-0.4).sp)
125
+ val Question = TextStyle(Feather, fontWeight = FontWeight.Bold, fontSize = 22.sp, lineHeight = 28.sp, letterSpacing = (-0.2).sp)
126
+ val HudNumber = TextStyle(Feather, fontWeight = FontWeight.Bold, fontSize = 18.sp, lineHeight = 18.sp, letterSpacing = (-0.1).sp)
127
+ val SectionHeader = TextStyle(DinRounded, fontWeight = FontWeight.Black, fontSize = 20.sp, lineHeight = 24.sp, letterSpacing = (-0.2).sp)
128
+ val SkillLabel = TextStyle(DinRounded, fontWeight = FontWeight.Black, fontSize = 16.sp, lineHeight = 19.sp)
129
+ val Button = TextStyle(DinRounded, fontWeight = FontWeight.Black, fontSize = 17.sp, lineHeight = 17.sp, letterSpacing = 0.4.sp)
130
+ val Tile = TextStyle(DinRounded, fontWeight = FontWeight.Bold, fontSize = 17.sp, lineHeight = 20.sp)
131
+ val Body = TextStyle(DinRounded, fontWeight = FontWeight.Normal, fontSize = 16.sp, lineHeight = 22.sp)
132
+ val Helper = TextStyle(DinRounded, fontWeight = FontWeight.Medium, fontSize = 14.sp, lineHeight = 18.sp)
133
+ val Meta = TextStyle(DinRounded, fontWeight = FontWeight.Medium, fontSize = 13.sp, lineHeight = 16.sp)
134
+ val Tab = TextStyle(DinRounded, fontWeight = FontWeight.Bold, fontSize = 11.sp, lineHeight = 11.sp, letterSpacing = 0.2.sp)
135
+ val LabelUpper = TextStyle(DinRounded, fontWeight = FontWeight.Black, fontSize = 12.sp, lineHeight = 14.sp, letterSpacing = 1.0.sp)
136
+ }
137
+
138
+ val DuolingoTypography = Typography(
139
+ displayLarge = DuolingoText.HeroNumber,
140
+ headlineLarge = DuolingoText.ScreenTitle,
141
+ titleLarge = DuolingoText.SectionHeader,
142
+ titleMedium = DuolingoText.SkillLabel,
143
+ bodyMedium = DuolingoText.Body,
144
+ labelLarge = DuolingoText.Button,
145
+ labelSmall = DuolingoText.Tab,
146
+ )
147
+ ```
148
+
149
+ ## 3. Signature Components
150
+
151
+ ### The 3D Primary Button (the Green Slab)
152
+
153
+ The single most recognizable Duolingo component: a chunky button on a 4dp darker-tone ledge. On press, the top face translates down 4dp to meet the ledge — the ledge "disappears." Mimics a stiff arcade button.
154
+
155
+ ```kotlin
156
+ import androidx.compose.animation.core.animateDpAsState
157
+ import androidx.compose.animation.core.spring
158
+ import androidx.compose.animation.core.tween
159
+ import androidx.compose.foundation.background
160
+ import androidx.compose.foundation.border
161
+ import androidx.compose.foundation.interaction.MutableInteractionSource
162
+ import androidx.compose.foundation.interaction.collectIsPressedAsState
163
+ import androidx.compose.foundation.layout.*
164
+ import androidx.compose.foundation.shape.RoundedCornerShape
165
+ import androidx.compose.foundation.clickable
166
+ import androidx.compose.material3.Text
167
+ import androidx.compose.runtime.*
168
+ import androidx.compose.ui.Alignment
169
+ import androidx.compose.ui.Modifier
170
+ import androidx.compose.ui.draw.clip
171
+ import androidx.compose.ui.graphics.Color
172
+ import androidx.compose.ui.hapticfeedback.HapticFeedbackType
173
+ import androidx.compose.ui.platform.LocalHapticFeedback
174
+ import androidx.compose.ui.unit.dp
175
+
176
+ enum class DuoButtonVariant { Primary, Neutral, Destructive }
177
+
178
+ @Composable
179
+ fun DuoPrimaryButton(
180
+ title: String,
181
+ onClick: () -> Unit,
182
+ modifier: Modifier = Modifier,
183
+ variant: DuoButtonVariant = DuoButtonVariant.Primary,
184
+ ) {
185
+ val (top, ledge, text) = when (variant) {
186
+ DuoButtonVariant.Primary -> Triple(DuolingoColors.FeatherGreen, DuolingoColors.ButtonGreenLedge, Color.White)
187
+ DuoButtonVariant.Neutral -> Triple(DuolingoColors.Snow, DuolingoColors.Swan, DuolingoColors.Eel)
188
+ DuoButtonVariant.Destructive -> Triple(DuolingoColors.CardinalRed, DuolingoColors.CardinalLedge, Color.White)
189
+ }
190
+ val interaction = remember { MutableInteractionSource() }
191
+ val pressed by interaction.collectIsPressedAsState()
192
+ val faceOffset by animateDpAsState(
193
+ targetValue = if (pressed) 4.dp else 0.dp,
194
+ animationSpec = if (pressed) tween(80) else spring(dampingRatio = 0.7f, stiffness = 500f),
195
+ label = "slab",
196
+ )
197
+ val haptics = LocalHapticFeedback.current
198
+
199
+ Box(modifier.height(60.dp)) { // 56dp face + 4dp ledge reveal
200
+ Box(
201
+ Modifier
202
+ .fillMaxWidth()
203
+ .height(56.dp)
204
+ .align(Alignment.BottomCenter)
205
+ .clip(RoundedCornerShape(16.dp))
206
+ .background(ledge),
207
+ )
208
+ Box(
209
+ Modifier
210
+ .fillMaxWidth()
211
+ .height(56.dp)
212
+ .offset(y = faceOffset)
213
+ .clip(RoundedCornerShape(16.dp))
214
+ .background(top)
215
+ .then(if (variant == DuoButtonVariant.Neutral) Modifier.border(2.dp, DuolingoColors.Swan, RoundedCornerShape(16.dp)) else Modifier)
216
+ .clickable(interaction, indication = null) {
217
+ haptics.performHapticFeedback(HapticFeedbackType.TextHandleMove) // ~iOS soft impact
218
+ onClick()
219
+ },
220
+ contentAlignment = Alignment.Center,
221
+ ) {
222
+ Text(title.uppercase(), style = DuolingoText.Button, color = text)
223
+ }
224
+ }
225
+ }
226
+ ```
227
+
228
+ ### Skill Node (Path Circle)
229
+
230
+ ```kotlin
231
+ import androidx.compose.animation.core.FastOutSlowInEasing
232
+ import androidx.compose.animation.core.RepeatMode
233
+ import androidx.compose.animation.core.animateFloat
234
+ import androidx.compose.animation.core.infiniteRepeatable
235
+ import androidx.compose.animation.core.rememberInfiniteTransition
236
+ import androidx.compose.foundation.shape.CircleShape
237
+ import androidx.compose.material.icons.Icons
238
+ import androidx.compose.material.icons.filled.Lock
239
+ import androidx.compose.material.icons.filled.Star
240
+ import androidx.compose.material.icons.filled.WorkspacePremium
241
+ import androidx.compose.material3.Icon
242
+ import androidx.compose.ui.draw.scale
243
+ import androidx.compose.ui.graphics.vector.ImageVector
244
+ import androidx.compose.ui.unit.dp
245
+
246
+ enum class SkillState { Locked, Available, Current, Completed, Gold, Legendary }
247
+
248
+ @Composable
249
+ fun SkillNode(
250
+ icon: ImageVector,
251
+ state: SkillState,
252
+ crownCount: Int,
253
+ onClick: () -> Unit,
254
+ modifier: Modifier = Modifier,
255
+ ) {
256
+ val size = if (state == SkillState.Current) 96.dp else 80.dp
257
+ val top = when (state) {
258
+ SkillState.Locked -> DuolingoColors.Swan
259
+ SkillState.Gold -> DuolingoColors.BeeYellow
260
+ SkillState.Legendary -> DuolingoColors.BeetlePurple
261
+ else -> DuolingoColors.FeatherGreen
262
+ }
263
+ val ledge = when (state) {
264
+ SkillState.Locked -> DuolingoColors.SwanLedge
265
+ SkillState.Gold -> DuolingoColors.GoldLedge
266
+ SkillState.Legendary -> Color(0xFFA05BD6)
267
+ else -> DuolingoColors.ButtonGreenLedge
268
+ }
269
+ val interaction = remember { MutableInteractionSource() }
270
+ val pressed by interaction.collectIsPressedAsState()
271
+ val pressScale by androidx.compose.animation.core.animateFloatAsState(if (pressed) 0.96f else 1f, spring(dampingRatio = 0.7f), label = "node")
272
+ val haptics = LocalHapticFeedback.current
273
+
274
+ // current node pulse ring
275
+ val ring = if (state == SkillState.Current) {
276
+ rememberInfiniteTransition(label = "pulse").animateFloat(
277
+ 1f, 1.12f, infiniteRepeatable(tween(900, easing = FastOutSlowInEasing), RepeatMode.Reverse), label = "ring",
278
+ ).value
279
+ } else 1f
280
+
281
+ Box(modifier.size(size + 4.dp), contentAlignment = Alignment.Center) {
282
+ if (state == SkillState.Current) {
283
+ Box(Modifier.size(size).scale(ring).clip(CircleShape).border(4.dp, DuolingoColors.MaskGreen, CircleShape))
284
+ }
285
+ Box(Modifier.size(size).offset(y = 4.dp).clip(CircleShape).background(ledge))
286
+ Box(
287
+ Modifier
288
+ .size(size)
289
+ .scale(pressScale)
290
+ .clip(CircleShape)
291
+ .background(top)
292
+ .clickable(interaction, indication = null) {
293
+ haptics.performHapticFeedback(HapticFeedbackType.TextHandleMove)
294
+ onClick()
295
+ },
296
+ contentAlignment = Alignment.Center,
297
+ ) {
298
+ Icon(
299
+ if (state == SkillState.Locked) Icons.Filled.Lock else icon,
300
+ contentDescription = null,
301
+ tint = if (state == SkillState.Locked) DuolingoColors.Hare else Color.White,
302
+ modifier = Modifier.size(size * 0.4f),
303
+ )
304
+ }
305
+ if (crownCount > 0 && state != SkillState.Locked) {
306
+ Row(
307
+ Modifier
308
+ .align(Alignment.TopCenter)
309
+ .offset(y = (-14).dp)
310
+ .clip(RoundedCornerShape(50))
311
+ .background(DuolingoColors.Eel)
312
+ .padding(horizontal = 8.dp, vertical = 3.dp),
313
+ verticalAlignment = Alignment.CenterVertically,
314
+ horizontalArrangement = Arrangement.spacedBy(2.dp),
315
+ ) {
316
+ Icon(Icons.Filled.WorkspacePremium, null, tint = DuolingoColors.BeeYellow, modifier = Modifier.size(12.dp))
317
+ Text("$crownCount", style = DuolingoText.Meta, color = Color.White)
318
+ }
319
+ }
320
+ }
321
+ }
322
+ ```
323
+
324
+ ### Answer Tile
325
+
326
+ ```kotlin
327
+ import androidx.compose.animation.core.Animatable
328
+ import androidx.compose.ui.draw.absoluteOffset
329
+ import kotlinx.coroutines.launch
330
+
331
+ enum class TileState { Idle, Selected, Correct, Wrong }
332
+
333
+ @Composable
334
+ fun AnswerTile(word: String, state: TileState, onTap: () -> Unit, modifier: Modifier = Modifier) {
335
+ val fill = when (state) {
336
+ TileState.Idle -> DuolingoColors.Snow
337
+ TileState.Selected -> DuolingoColors.TileSelectedFill
338
+ TileState.Correct -> DuolingoColors.TileCorrectFill
339
+ TileState.Wrong -> DuolingoColors.TileWrongFill
340
+ }
341
+ val border = when (state) {
342
+ TileState.Idle -> DuolingoColors.Swan
343
+ TileState.Selected -> DuolingoColors.TileSelectedBorder
344
+ TileState.Correct -> DuolingoColors.FeatherGreen
345
+ TileState.Wrong -> DuolingoColors.CardinalRed
346
+ }
347
+ val textColor = when (state) {
348
+ TileState.Idle, TileState.Selected -> DuolingoColors.Eel
349
+ TileState.Correct -> DuolingoColors.ButtonGreenLedge
350
+ TileState.Wrong -> DuolingoColors.CardinalRed
351
+ }
352
+ val shake = remember { Animatable(0f) }
353
+ val scope = rememberCoroutineScope()
354
+ val haptics = LocalHapticFeedback.current
355
+
356
+ LaunchedEffect(state) {
357
+ when (state) {
358
+ TileState.Wrong -> {
359
+ haptics.performHapticFeedback(HapticFeedbackType.LongPress) // ~iOS warning
360
+ scope.launch {
361
+ repeat(4) { shake.animateTo(if (it % 2 == 0) 6f else -6f, tween(50)) }
362
+ shake.animateTo(0f, tween(50))
363
+ }
364
+ }
365
+ TileState.Correct -> haptics.performHapticFeedback(HapticFeedbackType.LongPress) // ~iOS success
366
+ else -> {}
367
+ }
368
+ }
369
+
370
+ Box(modifier.absoluteOffset(x = shake.value.dp).height(52.dp)) {
371
+ Box(Modifier.fillMaxWidth().height(48.dp).align(Alignment.BottomCenter).clip(RoundedCornerShape(12.dp)).background(if (state == TileState.Idle) DuolingoColors.Swan else border))
372
+ Box(
373
+ Modifier
374
+ .fillMaxWidth()
375
+ .height(48.dp)
376
+ .clip(RoundedCornerShape(12.dp))
377
+ .background(fill)
378
+ .border(2.dp, border, RoundedCornerShape(12.dp))
379
+ .clickable(onClick = onTap)
380
+ .padding(horizontal = 20.dp),
381
+ contentAlignment = Alignment.Center,
382
+ ) {
383
+ Text(word, style = DuolingoText.Tile, color = textColor)
384
+ }
385
+ }
386
+ }
387
+ ```
388
+
389
+ ### HUD Chip (Streak / Gems / Hearts)
390
+
391
+ ```kotlin
392
+ import androidx.compose.material.icons.filled.Diamond
393
+ import androidx.compose.material.icons.filled.Favorite
394
+ import androidx.compose.material.icons.filled.LocalFireDepartment
395
+
396
+ enum class HudKind { Streak, Gems, Hearts }
397
+
398
+ @Composable
399
+ fun DuoHudChip(kind: HudKind, value: Int, modifier: Modifier = Modifier) {
400
+ val (icon, tint) = when (kind) {
401
+ HudKind.Streak -> Icons.Filled.LocalFireDepartment to DuolingoColors.FoxOrange
402
+ HudKind.Gems -> Icons.Filled.Diamond to DuolingoColors.MacawBlue
403
+ HudKind.Hearts -> Icons.Filled.Favorite to DuolingoColors.CardinalRed
404
+ }
405
+ Row(
406
+ modifier.defaultMinSize(minHeight = 32.dp).padding(horizontal = 10.dp, vertical = 4.dp),
407
+ verticalAlignment = Alignment.CenterVertically,
408
+ horizontalArrangement = Arrangement.spacedBy(6.dp),
409
+ ) {
410
+ Icon(icon, contentDescription = kind.name, tint = tint, modifier = Modifier.size(20.dp))
411
+ Text("$value", style = DuolingoText.HudNumber, color = DuolingoColors.Eel)
412
+ }
413
+ }
414
+ ```
415
+
416
+ ### Celebration Takeover
417
+
418
+ ```kotlin
419
+ @Composable
420
+ fun DuoCelebration(xpEarned: Int, onContinue: () -> Unit, modifier: Modifier = Modifier) {
421
+ val haptics = LocalHapticFeedback.current
422
+ LaunchedEffect(Unit) { haptics.performHapticFeedback(HapticFeedbackType.LongPress) } // ~iOS .success
423
+
424
+ Box(modifier.fillMaxSize().background(DuolingoColors.FeatherGreen)) {
425
+ Column(
426
+ Modifier.fillMaxSize().padding(top = 80.dp, bottom = 16.dp).padding(horizontal = 16.dp),
427
+ horizontalAlignment = Alignment.CenterHorizontally,
428
+ verticalArrangement = Arrangement.spacedBy(24.dp),
429
+ ) {
430
+ // Image(painterResource(R.drawable.duo_cheering), …) — bundled illustration, ~280dp
431
+ Box(Modifier.size(280.dp)) // placeholder slot for the Duo asset
432
+ Text("LESSON COMPLETE!", style = DuolingoText.ScreenTitle, color = Color.White)
433
+ Text(
434
+ "+$xpEarned XP",
435
+ style = DuolingoText.Question,
436
+ color = Color.White,
437
+ modifier = Modifier
438
+ .clip(RoundedCornerShape(50))
439
+ .background(DuolingoColors.BeeYellow.copy(alpha = 0.25f))
440
+ .border(2.dp, DuolingoColors.BeeYellow, RoundedCornerShape(50))
441
+ .padding(horizontal = 20.dp, vertical = 8.dp),
442
+ )
443
+ Spacer(Modifier.weight(1f))
444
+ DuoPrimaryButton("Continue", onContinue, variant = DuoButtonVariant.Neutral)
445
+ }
446
+ ConfettiLayer() // §6
447
+ }
448
+ }
449
+ ```
450
+
451
+ ## 4. The Learning Path (the signature dynamic)
452
+
453
+ Duolingo has no color extraction — its distinctive dynamic system is the **curved vertical learning path** (a left-right ±60dp wave of skill nodes, never a straight list) plus the **mascot/streak gamification** layered on top. Build the wave by offsetting each node by a `sin` phase inside a `LazyColumn`.
454
+
455
+ ```kotlin
456
+ import androidx.compose.foundation.lazy.LazyColumn
457
+ import androidx.compose.foundation.lazy.itemsIndexed
458
+ import kotlin.math.sin
459
+
460
+ data class PathNode(val id: String, val icon: ImageVector, val title: String, val state: SkillState, val crowns: Int)
461
+
462
+ @Composable
463
+ fun LearningPath(nodes: List<PathNode>, onNodeTap: (PathNode) -> Unit, modifier: Modifier = Modifier) {
464
+ LazyColumn(
465
+ modifier.fillMaxSize().background(DuolingoColors.Snow),
466
+ contentPadding = PaddingValues(vertical = 32.dp, horizontal = 16.dp),
467
+ verticalArrangement = Arrangement.spacedBy(32.dp),
468
+ horizontalAlignment = Alignment.CenterHorizontally,
469
+ ) {
470
+ itemsIndexed(nodes, key = { _, n -> n.id }) { index, node ->
471
+ // Alternating ±60dp wave — the recognizable Duolingo signature
472
+ val waveOffset = (sin(index * Math.PI / 3) * 60).dp
473
+ Column(
474
+ Modifier.offset(x = waveOffset),
475
+ horizontalAlignment = Alignment.CenterHorizontally,
476
+ verticalArrangement = Arrangement.spacedBy(8.dp),
477
+ ) {
478
+ SkillNode(node.icon, node.state, node.crowns, { onNodeTap(node) })
479
+ Text(node.title, style = DuolingoText.SkillLabel, color = DuolingoColors.Eel)
480
+ }
481
+ }
482
+ }
483
+ }
484
+ ```
485
+
486
+ The **streak flame** is the heart of the habit loop — idle gentle pulse, big confetti burst on a save-moment:
487
+
488
+ ```kotlin
489
+ @Composable
490
+ fun StreakFlame(count: Int, modifier: Modifier = Modifier) {
491
+ val t = rememberInfiniteTransition(label = "flame")
492
+ val scale by t.animateFloat(
493
+ 1f, 1.05f, infiniteRepeatable(tween(2000, easing = FastOutSlowInEasing), RepeatMode.Reverse), label = "idle",
494
+ )
495
+ Row(modifier, verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(4.dp)) {
496
+ Icon(
497
+ Icons.Filled.LocalFireDepartment, "Streak",
498
+ tint = DuolingoColors.FoxOrange,
499
+ modifier = Modifier.size(20.dp).scale(scale),
500
+ )
501
+ Text("$count", style = DuolingoText.HudNumber, color = DuolingoColors.FoxOrange)
502
+ }
503
+ }
504
+ ```
505
+
506
+ ## 5. Navigation
507
+
508
+ Duolingo's root is a **5-tab bottom bar** (Learn / Leaderboard / Quests / Shop / Profile). The current iOS app is icon-only by default with a soft green tint pill behind the active tab. Active tint is Feather Green. Android has no live blur — the bar is an opaque snow surface with a hairline top divider.
509
+
510
+ ```kotlin
511
+ import androidx.compose.material.icons.filled.EmojiEvents
512
+ import androidx.compose.material.icons.filled.Home
513
+ import androidx.compose.material.icons.filled.Person
514
+ import androidx.compose.material.icons.filled.ShoppingCart
515
+ import androidx.compose.material.icons.filled.Description
516
+ import androidx.compose.material3.NavigationBar
517
+ import androidx.compose.material3.NavigationBarItem
518
+ import androidx.compose.material3.NavigationBarItemDefaults
519
+
520
+ @Composable
521
+ fun DuolingoBottomBar(selected: Int, onSelect: (Int) -> Unit) {
522
+ NavigationBar(containerColor = DuolingoColors.Snow, tonalElevation = 0.dp) {
523
+ val items = listOf(
524
+ "Learn" to Icons.Filled.Home,
525
+ "Leaderboard" to Icons.Filled.EmojiEvents,
526
+ "Quests" to Icons.Filled.Description,
527
+ "Shop" to Icons.Filled.ShoppingCart,
528
+ "Profile" to Icons.Filled.Person,
529
+ )
530
+ items.forEachIndexed { i, (label, icon) ->
531
+ NavigationBarItem(
532
+ selected = selected == i,
533
+ onClick = { onSelect(i) },
534
+ icon = { Icon(icon, contentDescription = label, modifier = Modifier.size(28.dp)) },
535
+ colors = NavigationBarItemDefaults.colors(
536
+ selectedIconColor = DuolingoColors.FeatherGreen,
537
+ unselectedIconColor = DuolingoColors.Hare,
538
+ indicatorColor = Color(0xFFE5F8D5), // soft green tint pill behind active tab
539
+ ),
540
+ )
541
+ }
542
+ }
543
+ }
544
+ ```
545
+
546
+ ## 6. Motion & Effects
547
+
548
+ | Moment | Compose recipe |
549
+ |--------|----------------|
550
+ | Button press | top face `animateDpAsState` 0 → 4dp over 80ms `tween`; release `spring(dampingRatio = 0.7f)`; `HapticFeedbackType.TextHandleMove` (soft) |
551
+ | Correct answer | tile → green + checkmark `scale` 0 → 1.2 → 1.0 spring (300ms); success haptic + chime |
552
+ | Wrong answer | tile → red + horizontal `Animatable` shake ±6dp for ~400ms; warning haptic + buzz |
553
+ | Skill node tap | `scale` 0.96 → 1.0 spring (≈180ms); light haptic; if unlocking, play celebration |
554
+ | Level complete | crossfade to green canvas 300ms, Duo slides up `spring` 500ms, confetti 1.2s, success haptic |
555
+ | Streak flame | idle infinite `scale` 1.0 ↔ 1.05 over 2s; on save: `Animatable` burst 1.3 → 1.0 + confetti |
556
+ | XP ring fill | `Animatable` stroke sweep 0 → target over 800ms `ease-out`, haptic each quarter |
557
+
558
+ ### Confetti Layer
559
+
560
+ ```kotlin
561
+ import androidx.compose.foundation.Canvas
562
+ import androidx.compose.ui.geometry.Offset
563
+ import androidx.compose.ui.geometry.Size
564
+ import androidx.compose.ui.graphics.drawscope.rotate
565
+ import kotlin.random.Random
566
+
567
+ @Composable
568
+ fun ConfettiLayer(modifier: Modifier = Modifier) {
569
+ data class Piece(val x: Float, val color: Color, val delay: Int, val spin: Float)
570
+ val palette = listOf(
571
+ DuolingoColors.FeatherGreen, DuolingoColors.FoxOrange, DuolingoColors.BeeYellow,
572
+ DuolingoColors.MacawBlue, DuolingoColors.CardinalRed, DuolingoColors.BeetlePurple,
573
+ )
574
+ val pieces = remember {
575
+ List(12) { Piece(Random.nextFloat(), palette.random(), Random.nextInt(0, 300), Random.nextFloat() * 720f - 360f) }
576
+ }
577
+ val progress = remember { Animatable(0f) }
578
+ LaunchedEffect(Unit) { progress.animateTo(1f, tween(1200)) }
579
+
580
+ Canvas(modifier.fillMaxSize()) {
581
+ pieces.forEach { p ->
582
+ val t = ((progress.value * 1200f - p.delay) / 1200f).coerceIn(0f, 1f)
583
+ val y = t * (size.height + 40f) - 20f
584
+ rotate(p.spin * t, pivot = Offset(p.x * size.width, y)) {
585
+ drawRoundRect(
586
+ color = p.color,
587
+ topLeft = Offset(p.x * size.width - 4f, y),
588
+ size = Size(8.dp.toPx(), 14.dp.toPx()),
589
+ cornerRadius = androidx.compose.ui.geometry.CornerRadius(2.dp.toPx()),
590
+ )
591
+ }
592
+ }
593
+ }
594
+ }
595
+ ```
596
+
597
+ ### Haptics Summary
598
+
599
+ ```kotlin
600
+ // Correct answer → HapticFeedbackType.LongPress (~iOS .success) + chime SFX
601
+ // Wrong answer → HapticFeedbackType.LongPress (~iOS .warning) + buzz SFX
602
+ // Button press → HapticFeedbackType.TextHandleMove (~iOS .soft impact)
603
+ // Celebration → HapticFeedbackType.LongPress (~iOS .success) + cheer SFX
604
+ // Node unlock → HapticFeedbackType.LongPress (~iOS .medium impact)
605
+ ```
606
+
607
+ Pair every haptic with an SFX asset via `MediaPlayer`/`SoundPool` — the game feel needs both channels. For finer impact control use `LocalView.current.performHapticFeedback(HapticFeedbackConstants.CONFIRM)` (API 30+) or a `Vibrator` `VibrationEffect`.
608
+
609
+ ## 7. Icons
610
+
611
+ Duolingo ships proprietary glyphs (the Duo wordmark, league badges, Duo the owl). Use `androidx.compose.material:material-icons-extended` for the closest semantic match; bundle Duo illustrations + league badges as vector drawables / image assets loaded via `painterResource(R.drawable.…)`.
612
+
613
+ | Component | SF Symbol (iOS) | Material Icon (Compose) |
614
+ |-----------|-----------------|--------------------------|
615
+ | Streak flame | `flame.fill` | `Icons.Filled.LocalFireDepartment` |
616
+ | Gems | `diamond.fill` | `Icons.Filled.Diamond` |
617
+ | Hearts | `heart.fill` | `Icons.Filled.Favorite` |
618
+ | Locked skill | `lock.fill` | `Icons.Filled.Lock` |
619
+ | Completed skill | `star.fill` / `checkmark` | `Icons.Filled.Star` / `Icons.Filled.Check` |
620
+ | Speaking exercise | `waveform` | `Icons.Filled.GraphicEq` |
621
+ | Listening exercise | `ear.fill` | `Icons.Filled.Hearing` |
622
+ | Close lesson | `xmark` | `Icons.Filled.Close` |
623
+ | Continue | `arrow.right` | `Icons.Filled.ArrowForward` |
624
+ | Learn tab | `house.fill` | `Icons.Filled.Home` |
625
+ | Leaderboard tab | `trophy.fill` | `Icons.Filled.EmojiEvents` |
626
+ | Quests tab | `scroll.fill` | `Icons.Filled.Description` |
627
+ | Shop tab | `cart.fill` | `Icons.Filled.ShoppingCart` |
628
+ | Profile tab | `person.fill` | `Icons.Filled.Person` |
629
+ | Crown (mastery) | `crown.fill` | `Icons.Filled.WorkspacePremium` |
630
+ | Duo the owl / league badges | (custom) | Vector drawable / image asset — never substitute |
631
+
632
+ ## 8. Minimum SDK & Accessibility Notes
633
+
634
+ - **minSdk 24** (Compose floor is 21; modern motion is comfortable at 24). `compileSdk 34+`, Compose BOM `2024.09+`, Kotlin `2.0+`.
635
+ - **Edge-to-edge**: call `enableEdgeToEdge()` in the `Activity`. The snow canvas wants dark-content system bars (`isAppearanceLightStatusBars = true`); the HUD aligns chips to the trailing edge inside `windowInsetsPadding`, and the CHECK button sits just above the gesture nav.
636
+ - **Font scaling**: `sp` honors the user's font scale — scale the question prompt, body, and lesson explanations. Pin layout-sensitive text (uppercase button labels, HUD numbers, tab labels) by deriving from `dp`; skill-node labels scale to XL only. Uppercase button text is tight — pin it.
637
+ - **TalkBack**: label answer tiles with full text + state — `Modifier.semantics { contentDescription = "Answer tile: casa, selected" }`. On correct/wrong announce the toast ("Correct! +10 XP"). Skill nodes announce title + state ("Basics 1, completed, 3 crowns").
638
+ - **Touch targets**: Material guidance is 48dp minimum. The 56dp primary button and 80dp skill node clear easily; answer tiles are 48dp; icon buttons need 48dp hit area via padding around the 24dp glyph; HUD chips reach 48dp effective tap.
639
+ - **Contrast**: Feather Green `#58CC02` on white clears WCAG AA only at 14sp+ bold — always use **white text on green surfaces** for button labels, never green text on white. Eel `#4B4B4B` on white passes AAA. The real low-contrast pair is **Hare `#AFAFAF` on white** — validate at 13sp meta/helper and darken toward Wolf `#777777` if targeting compliance. Never signal correctness by red/green alone — pair with a checkmark/X glyph and the shake/pop animation.
640
+ - **Reduce motion**: when `Settings.Global.ANIMATOR_DURATION_SCALE == 0f`, disable the confetti layer, the flame idle pulse, and the current-node pulse ring; keep the button ledge collapse (it conveys state) but skip the spring overshoot.
641
+ - **Dynamic color**: do **not** enable Material You `dynamicLightColorScheme()` — Duolingo's deliberately-bright 6-role palette (green/red/orange/yellow/blue/purple) is brand-load-bearing and must not shift with wallpaper. Strong-brand app: keep the curated scheme.