Refactor components to use InlineSvg for icons, enhance Header layout with grid, and implement achievement system in FlagQuiz page

- Replaced inline SVGs with InlineSvg component in Filter, Header, and ThemeSwitcher components for better maintainability.
- Improved Header layout using CSS Grid for better alignment and spacing of elements.
- Added achievement tracking and display functionality in FlagQuiz, including reset confirmation dialog and achievement count updates.
- Enhanced user feedback with animated result icons in FlagQuiz for correct and wrong answers.
- Introduced country info tooltips in FlagQuiz for additional context on answers.
This commit is contained in:
sHa
2025-08-11 16:14:18 +03:00
parent f9182a6867
commit 292c7e88b6
42 changed files with 1348 additions and 167 deletions

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="4" stroke="currentColor" stroke-width="1.5"/>
<path d="M22 12C22 16.714 22 19.0711 20.5355 20.5355C19.0711 22 16.714 22 12 22C7.28595 22 4.92893 22 3.46447 20.5355C2 19.0711 2 16.714 2 12C2 7.28595 2 4.92893 3.46447 3.46447C4.92893 2 7.28595 2 12 2C16.714 2 19.0711 2 20.5355 3.46447C21.5093 4.43821 21.8356 5.80655 21.9449 8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 653 B

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.5 12.5L10.5 14.5L15.5 9.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M7 3.33782C8.47087 2.48697 10.1786 2 12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 10.1786 2.48697 8.47087 3.33782 7" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 597 B

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.5 12.5L10.5 14.5L15.5 9.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M22 12C22 16.714 22 19.0711 20.5355 20.5355C19.0711 22 16.714 22 12 22C7.28595 22 4.92893 22 3.46447 20.5355C2 19.0711 2 16.714 2 12C2 7.28595 2 4.92893 3.46447 3.46447C4.92893 2 7.28595 2 12 2C16.714 2 19.0711 2 20.5355 3.46447C21.5093 4.43821 21.8356 5.80655 21.9449 8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 709 B

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14.5 9.50002L9.5 14.5M9.49998 9.5L14.5 14.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<path d="M7 3.33782C8.47087 2.48697 10.1786 2 12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 10.1786 2.48697 8.47087 3.33782 7" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 589 B

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14.5 9.50002L9.5 14.5M9.49998 9.5L14.5 14.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<path d="M22 12C22 16.714 22 19.0711 20.5355 20.5355C19.0711 22 16.714 22 12 22C7.28595 22 4.92893 22 3.46447 20.5355C2 19.0711 2 16.714 2 12C2 7.28595 2 4.92893 3.46447 3.46447C4.92893 2 7.28595 2 12 2C16.714 2 19.0711 2 20.5355 3.46447C21.5093 4.43821 21.8356 5.80655 21.9449 8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 701 B

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13.3057 18.2975L8.23724 19.987C5.47183 20.9088 4.08912 21.3697 3.35924 20.6398C2.62936 19.9099 3.09026 18.5272 4.01207 15.7618L5.70156 10.6933C6.46758 8.39525 6.85059 7.24623 7.75684 7.03229C8.6631 6.81835 9.51953 7.67478 11.2324 9.38764L14.6114 12.7666C16.3242 14.4795 17.1807 15.3359 16.9667 16.2422" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<path d="M12.2351 18.3461C12.2351 18.3461 11.477 16.0649 11.477 14.5552C11.477 13.0454 12.2351 10.7643 12.2351 10.7643M8.06517 19.4833C8.06517 19.4833 7.42484 16.7314 7.307 14.9343C7.11229 11.965 8.06517 7.35254 8.06517 7.35254" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<path d="M14.5093 10.0061L14.6533 9.28614C14.7986 8.55924 15.3224 7.96597 16.0256 7.73155C16.7289 7.49714 17.2526 6.90387 17.398 6.17697L17.542 5.45703" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<path d="M17.5691 12.2533L17.7819 12.3762C18.4391 12.7556 19.2652 12.6719 19.8329 12.1685C20.347 11.7126 21.0792 11.597 21.7087 11.8723L22.0002 11.9997" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<path d="M10.5352 3C10.1977 3.55206 10.2823 4.26344 10.7399 4.72097L10.8377 4.81885C11.2309 5.21201 11.3759 5.78959 11.215 6.32182" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<path d="M13.561 4.39648C13.7621 4.19542 13.8626 4.09489 13.9788 4.05804C14.0772 4.02688 14.1827 4.02688 14.281 4.05804C14.3973 4.09489 14.4978 4.19542 14.6989 4.39648C14.8999 4.59753 15.0004 4.69806 15.0373 4.8143C15.0685 4.91262 15.0685 5.01817 15.0373 5.11648C15.0004 5.23272 14.8999 5.33325 14.6989 5.53431C14.4978 5.73536 14.3973 5.83589 14.281 5.87274C14.1827 5.90391 14.0772 5.90391 13.9788 5.87274C13.8626 5.83589 13.7621 5.73536 13.561 5.53431C13.36 5.33325 13.2594 5.23272 13.2226 5.11648C13.1914 5.01817 13.1914 4.91262 13.2226 4.8143C13.2594 4.69806 13.36 4.59753 13.561 4.39648Z" fill="currentColor"/>
<path d="M19.4682 7.46822C19.7136 7.22283 19.8363 7.10014 19.9747 7.04681C20.1367 6.9844 20.3161 6.9844 20.4781 7.04681C20.6165 7.10014 20.7392 7.22283 20.9846 7.46822C21.23 7.71362 21.3527 7.83631 21.406 7.97472C21.4684 8.1367 21.4684 8.31609 21.406 8.47807C21.3527 8.61649 21.23 8.73918 20.9846 8.98457C20.7392 9.22996 20.6165 9.35266 20.4781 9.40599C20.3161 9.4684 20.1367 9.4684 19.9747 9.40599C19.8363 9.35266 19.7136 9.22996 19.4682 8.98457C19.2228 8.73918 19.1001 8.61649 19.0468 8.47807C18.9844 8.31609 18.9844 8.1367 19.0468 7.97472C19.1001 7.83631 19.2228 7.71362 19.4682 7.46822Z" fill="currentColor"/>
<path d="M6.92737 3.94079C7.13683 3.73132 7.47645 3.73132 7.68592 3.94079C7.89539 4.15026 7.89539 4.48988 7.68592 4.69935C7.47645 4.90882 7.13683 4.90882 6.92737 4.69935C6.7179 4.48988 6.7179 4.15026 6.92737 3.94079Z" fill="currentColor"/>
<path d="M19.0582 15.3134C19.2677 15.1039 19.6073 15.1039 19.8168 15.3134C20.0262 15.5228 20.0262 15.8624 19.8168 16.0719C19.6073 16.2814 19.2677 16.2814 19.0582 16.0719C18.8488 15.8624 18.8488 15.5228 19.0582 15.3134Z" fill="currentColor"/>
<path d="M17.5 9.74145C17.7095 9.53198 18.0491 9.53198 18.2586 9.74145C18.468 9.95092 18.468 10.2905 18.2586 10.5C18.0491 10.7095 17.7095 10.7095 17.5 10.5C17.2905 10.2905 17.2905 9.95092 17.5 9.74145Z" fill="currentColor"/>
</svg>

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M21.8382 11.1263C22.0182 9.2137 22.1082 8.25739 21.781 7.86207C21.604 7.64823 21.3633 7.5172 21.106 7.4946C20.6303 7.45282 20.0329 8.1329 18.8381 9.49307C18.2202 10.1965 17.9113 10.5482 17.5666 10.6027C17.3757 10.6328 17.1811 10.6018 17.0047 10.5131C16.6865 10.3529 16.4743 9.91812 16.0499 9.04851L13.8131 4.46485C13.0112 2.82162 12.6102 2 12 2C11.3898 2 10.9888 2.82162 10.1869 4.46486L7.95007 9.04852C7.5257 9.91812 7.31351 10.3529 6.99526 10.5131C6.81892 10.6018 6.62434 10.6328 6.43337 10.6027C6.08872 10.5482 5.77977 10.1965 5.16187 9.49307C3.96708 8.1329 3.36968 7.45282 2.89399 7.4946C2.63666 7.5172 2.39598 7.64823 2.21899 7.86207C1.8918 8.25739 1.9818 9.2137 2.16181 11.1263L2.391 13.5616C2.76865 17.5742 2.95748 19.5805 4.14009 20.7902C5.32271 22 7.09517 22 10.6401 22H13.3599C16.9048 22 18.6773 22 19.8599 20.7902C20.7738 19.8553 21.0942 18.4447 21.367 16" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11 8L12.5 6.5V10.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M19 5L19.9486 5.31621C20.9387 5.64623 21.4337 5.81124 21.7168 6.20408C22 6.59692 22 7.11873 21.9999 8.16234L21.9999 8.23487C21.9999 9.09561 21.9999 9.52598 21.7927 9.87809C21.5855 10.2302 21.2093 10.4392 20.4569 10.8572L17.5 12.5" stroke="currentColor" stroke-width="1.5"/>
<path d="M4.99994 5L4.05132 5.31621C3.06126 5.64623 2.56623 5.81124 2.2831 6.20408C1.99996 6.59692 1.99997 7.11873 2 8.16234L2 8.23487C2.00003 9.09561 2.00004 9.52598 2.20723 9.87809C2.41441 10.2302 2.79063 10.4392 3.54305 10.8572L6.49994 12.5" stroke="currentColor" stroke-width="1.5"/>
<path d="M12 16V19" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<path d="M15.5 22H8.5L8.83922 20.3039C8.93271 19.8365 9.34312 19.5 9.8198 19.5H14.1802C14.6569 19.5 15.0673 19.8365 15.1608 20.3039L15.5 22Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M18 22H6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<path d="M17 2.45597C17.7415 2.59747 18.1811 2.75299 18.5609 3.22083C19.0367 3.80673 19.0115 4.43998 18.9612 5.70647C18.7805 10.2595 17.7601 16 12.0002 16C6.24021 16 5.21983 10.2595 5.03907 5.70647C4.98879 4.43998 4.96365 3.80673 5.43937 3.22083C5.91508 2.63494 6.48445 2.53887 7.62318 2.34674C8.74724 2.15709 10.2166 2 12.0002 2C12.7184 2 13.3857 2.02548 14 2.06829" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

10
public/icons/cup-star.svg Normal file
View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 16V19" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<path d="M15.5 22H8.5L8.83922 20.3039C8.93271 19.8365 9.34312 19.5 9.8198 19.5H14.1802C14.6569 19.5 15.0673 19.8365 15.1608 20.3039L15.5 22Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M19 5L19.9486 5.31621C20.9387 5.64623 21.4337 5.81124 21.7168 6.20408C22 6.59692 22 7.11873 21.9999 8.16234L21.9999 8.23487C21.9999 9.09561 21.9999 9.52598 21.7927 9.87809C21.5855 10.2302 21.2093 10.4392 20.4569 10.8572L17.5 12.5" stroke="currentColor" stroke-width="1.5"/>
<path d="M4.99994 5L4.05132 5.31621C3.06126 5.64623 2.56623 5.81124 2.2831 6.20408C1.99996 6.59692 1.99997 7.11873 2 8.16234L2 8.23487C2.00003 9.09561 2.00004 9.52598 2.20723 9.87809C2.41441 10.2302 2.79063 10.4392 3.54305 10.8572L6.49994 12.5" stroke="currentColor" stroke-width="1.5"/>
<path d="M11.1459 6.02251C11.5259 5.34084 11.7159 5 12 5C12.2841 5 12.4741 5.34084 12.8541 6.02251L12.9524 6.19887C13.0603 6.39258 13.1143 6.48944 13.1985 6.55334C13.2827 6.61725 13.3875 6.64097 13.5972 6.68841L13.7881 6.73161C14.526 6.89857 14.895 6.98205 14.9828 7.26432C15.0706 7.54659 14.819 7.84072 14.316 8.42898L14.1858 8.58117C14.0429 8.74833 13.9714 8.83191 13.9392 8.93531C13.9071 9.03872 13.9179 9.15023 13.9395 9.37327L13.9592 9.57632C14.0352 10.3612 14.0733 10.7536 13.8435 10.9281C13.6136 11.1025 13.2682 10.9435 12.5773 10.6254L12.3986 10.5431C12.2022 10.4527 12.1041 10.4075 12 10.4075C11.8959 10.4075 11.7978 10.4527 11.6014 10.5431L11.4227 10.6254C10.7318 10.9435 10.3864 11.1025 10.1565 10.9281C9.92674 10.7536 9.96476 10.3612 10.0408 9.57632L10.0605 9.37327C10.0821 9.15023 10.0929 9.03872 10.0608 8.93531C10.0286 8.83191 9.95713 8.74833 9.81418 8.58117L9.68403 8.42898C9.18097 7.84072 8.92945 7.54659 9.01723 7.26432C9.10501 6.98205 9.47396 6.89857 10.2119 6.73161L10.4028 6.68841C10.6125 6.64097 10.7173 6.61725 10.8015 6.55334C10.8857 6.48944 10.9397 6.39258 11.0476 6.19887L11.1459 6.02251Z" stroke="currentColor" stroke-width="1.5"/>
<path d="M18 22H6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<path d="M17 2.45597C17.7415 2.59747 18.1811 2.75299 18.5609 3.22083C19.0367 3.80673 19.0115 4.43998 18.9612 5.70647C18.7805 10.2595 17.7601 16 12.0002 16C6.24021 16 5.21983 10.2595 5.03907 5.70647C4.98879 4.43998 4.96365 3.80673 5.43937 3.22083C5.91508 2.63494 6.48445 2.53887 7.62318 2.34674C8.74724 2.15709 10.2166 2 12.0002 2C12.7184 2 13.3857 2.02548 14 2.06829" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

9
public/icons/cup.svg Normal file
View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M17 2.45597C17.7415 2.59747 18.1811 2.75299 18.5609 3.22083C19.0367 3.80673 19.0115 4.43998 18.9612 5.70647C18.7805 10.2595 17.7601 16 12.0002 16C6.24021 16 5.21983 10.2595 5.03907 5.70647C4.98879 4.43998 4.96365 3.80673 5.43937 3.22083C5.91508 2.63494 6.48445 2.53887 7.62318 2.34674C8.74724 2.15709 10.2166 2 12.0002 2C12.7184 2 13.3857 2.02548 14 2.06829" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<path d="M19 5L19.9486 5.31621C20.9387 5.64623 21.4337 5.81124 21.7168 6.20408C22 6.59692 22 7.11873 21.9999 8.16234L21.9999 8.23487C21.9999 9.09561 21.9999 9.52598 21.7927 9.87809C21.5855 10.2302 21.2093 10.4392 20.4569 10.8572L17.5 12.5" stroke="currentColor" stroke-width="1.5"/>
<path d="M4.99994 5L4.05132 5.31621C3.06126 5.64623 2.56623 5.81124 2.2831 6.20408C1.99996 6.59692 1.99997 7.11873 2 8.16234L2 8.23487C2.00003 9.09561 2.00004 9.52598 2.20723 9.87809C2.41441 10.2302 2.79063 10.4392 3.54305 10.8572L6.49994 12.5" stroke="currentColor" stroke-width="1.5"/>
<path d="M12 17V19" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<path d="M15.5 22H8.5L8.83922 20.3039C8.93271 19.8365 9.34312 19.5 9.8198 19.5H14.1802C14.6569 19.5 15.0673 19.8365 15.1608 20.3039L15.5 22Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M18 22H6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 7V13" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<circle cx="12" cy="16" r="1" fill="currentColor"/>
<path d="M22 12C22 16.714 22 19.0711 20.5355 20.5355C19.0711 22 16.714 22 12 22C7.28595 22 4.92893 22 3.46447 20.5355C2 19.0711 2 16.714 2 12C2 7.28595 2 4.92893 3.46447 3.46447C4.92893 2 7.28595 2 12 2C16.714 2 19.0711 2 20.5355 3.46447C21.5093 4.43821 21.8356 5.80655 21.9449 8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 718 B

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14.5 9.50002L9.5 14.5M9.49998 9.5L14.5 14.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<path d="M22 12C22 16.714 22 19.0711 20.5355 20.5355C19.0711 22 16.714 22 12 22C7.28595 22 4.92893 22 3.46447 20.5355C2 19.0711 2 16.714 2 12C2 7.28595 2 4.92893 3.46447 3.46447C4.92893 2 7.28595 2 12 2C16.714 2 19.0711 2 20.5355 3.46447C21.5093 4.43821 21.8356 5.80655 21.9449 8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 701 B

3
public/icons/filter.svg Normal file
View File

@@ -0,0 +1,3 @@
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M20.058 9.72255C21.0065 9.18858 21.4808 8.9216 21.7404 8.49142C22 8.06124 22 7.54232 22 6.50448V5.81466C22 4.48782 22 3.8244 21.5607 3.4122C21.1213 3 20.4142 3 19 3H5C3.58579 3 2.87868 3 2.43934 3.4122C2 3.8244 2 4.48782 2 5.81466V6.50448C2 7.54232 2 8.06124 2.2596 8.49142C2.5192 8.9216 2.99347 9.18858 3.94202 9.72255L6.85504 11.3624C7.49146 11.7206 7.80967 11.8998 8.03751 12.0976C8.51199 12.5095 8.80408 12.9935 8.93644 13.5872C9 13.8722 9 14.2058 9 14.8729L9 17.5424C9 18.452 9 18.9067 9.25192 19.2613C9.50385 19.6158 9.95128 19.7907 10.8462 20.1406C12.7248 20.875 13.6641 21.2422 14.3321 20.8244C15 20.4066 15 19.4519 15 17.5424V14.8729C15 14.2058 15 13.8722 15.0636 13.5872C15.1959 12.9935 15.488 12.5095 15.9625 12.0976" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 916 B

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6 9.99739C6.01447 8.29083 6.10921 7.35004 6.72963 6.72963C7.35004 6.10921 8.29083 6.01447 9.99739 6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<path d="M6 14.0007C6.01447 15.7072 6.10921 16.648 6.72963 17.2684C7.35004 17.8888 8.29083 17.9836 9.99739 17.998" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<path d="M17.9976 9.99739C17.9831 8.29083 17.8883 7.35004 17.2679 6.72963C16.6475 6.10921 15.7067 6.01447 14.0002 6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<path d="M17.9976 14.0007C17.9831 15.7072 17.8883 16.648 17.2679 17.2684C16.6475 17.8888 15.7067 17.9836 14.0002 17.998" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<path d="M22 12C22 16.714 22 19.0711 20.5355 20.5355C19.0711 22 16.714 22 12 22C7.28595 22 4.92893 22 3.46447 20.5355C2 19.0711 2 16.714 2 12C2 7.28595 2 4.92893 3.46447 3.46447C4.92893 2 7.28595 2 12 2C16.714 2 19.0711 2 20.5355 3.46447C21.5093 4.43821 21.8356 5.80655 21.9449 8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

9
public/icons/gamepad.svg Normal file
View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M21.2206 8C20.5311 5.81603 19.4281 4.63486 18.0908 4.16059C17.7099 4.02549 17.3016 4 16.8974 4H16.2849C15.4074 4 14.5514 4.27225 13.8351 4.77922L13.3332 5.13441C12.9434 5.41029 12.4776 5.55844 12 5.55844C11.5225 5.55844 11.0567 5.41029 10.6669 5.13443L10.165 4.77922C9.44862 4.27225 8.59264 4 7.71504 4H7.10257C6.69838 4 6.29009 4.02549 5.90915 4.16059C3.52645 5.00566 1.88749 8.09504 2.00604 15.1026C2.02992 16.5145 2.3603 18.075 3.63423 18.6842C4.03121 18.8741 4.49667 19 5.02671 19C5.66273 19 6.1678 18.8187 6.55763 18.5632C7.47153 17.9642 8.14122 16.9639 9.11125 16.4609C9.69519 16.1581 10.3434 16 11.0011 16H12.9989C13.6566 16 14.3048 16.1581 14.8888 16.4609C15.8588 16.9639 16.5285 17.9642 17.4424 18.5632C17.8322 18.8187 18.3373 19 18.9733 19C19.5033 19 19.9688 18.8741 20.3658 18.6842C21.6397 18.075 21.9701 16.5145 21.994 15.1026C22.0132 13.9681 21.9863 12.9362 21.9176 12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<path d="M7.5 9V12M6 10.5L9 10.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<path d="M19 10.25C19 10.6642 18.6642 11 18.25 11C17.8358 11 17.5 10.6642 17.5 10.25C17.5 9.83579 17.8358 9.5 18.25 9.5C18.6642 9.5 19 9.83579 19 10.25Z" fill="currentColor"/>
<path d="M16 10.25C16 10.6642 15.6642 11 15.25 11C14.8358 11 14.5 10.6642 14.5 10.25C14.5 9.83579 14.8358 9.5 15.25 9.5C15.6642 9.5 16 9.83579 16 10.25Z" fill="currentColor"/>
<path d="M16.75 8C17.1642 8 17.5 8.33579 17.5 8.75C17.5 9.16421 17.1642 9.5 16.75 9.5C16.3358 9.5 16 9.16421 16 8.75C16 8.33579 16.3358 8 16.75 8Z" fill="currentColor"/>
<path d="M16.75 11C17.1642 11 17.5 11.3358 17.5 11.75C17.5 12.1642 17.1642 12.5 16.75 12.5C16.3358 12.5 16 12.1642 16 11.75C16 11.3358 16.3358 11 16.75 11Z" fill="currentColor"/>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1 @@
https://www.svgrepo.com/collection/solar-broken-line-icons/

View File

@@ -0,0 +1,7 @@
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 17V11" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
<circle cx="1" cy="1" r="1" transform="matrix(1 0 0 -1 11 9)" fill="currentColor" />
<path
d="M7 3.33782C8.47087 2.48697 10.1786 2 12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 10.1786 2.48697 8.47087 3.33782 7"
stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
</svg>

After

Width:  |  Height:  |  Size: 552 B

View File

@@ -0,0 +1,7 @@
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 17V11" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
<circle cx="1" cy="1" r="1" transform="matrix(1 0 0 -1 11 9)" fill="currentColor" />
<path
d="M22 12C22 16.714 22 19.0711 20.5355 20.5355C19.0711 22 16.714 22 12 22C7.28595 22 4.92893 22 3.46447 20.5355C2 19.0711 2 16.714 2 12C2 7.28595 2 4.92893 3.46447 3.46447C4.92893 2 7.28595 2 12 2C16.714 2 19.0711 2 20.5355 3.46447C21.5093 4.43821 21.8356 5.80655 21.9449 8"
stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
</svg>

After

Width:  |  Height:  |  Size: 664 B

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6 10V8C6 7.65929 6.0284 7.32521 6.08296 7M18 10V8C18 4.68629 15.3137 2 12 2C10.208 2 8.59942 2.78563 7.5 4.03126" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<path d="M11 22H8C5.17157 22 3.75736 22 2.87868 21.1213C2 20.2426 2 18.8284 2 16C2 13.1716 2 11.7574 2.87868 10.8787C3.75736 10 5.17157 10 8 10H16C18.8284 10 20.2426 10 21.1213 10.8787C22 11.7574 22 13.1716 22 16C22 18.8284 22 20.2426 21.1213 21.1213C20.2426 22 18.8284 22 16 22H15" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 772 B

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11 22H8C5.17157 22 3.75736 22 2.87868 21.1213C2 20.2426 2 18.8284 2 16C2 13.1716 2 11.7574 2.87868 10.8787C3.75736 10 5.17157 10 8 10H16C18.8284 10 20.2426 10 21.1213 10.8787C22 11.7574 22 13.1716 22 16C22 18.8284 22 20.2426 21.1213 21.1213C20.2426 22 18.8284 22 16 22H15" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<path d="M6 10V8C6 7.65929 6.0284 7.32521 6.08296 7M17.811 6.5C17.1449 3.91216 14.7958 2 12 2C10.223 2 8.62643 2.7725 7.52779 4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 777 B

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.1459 7.02251C11.5259 6.34084 11.7159 6 12 6C12.2841 6 12.4741 6.34084 12.8541 7.02251L12.9524 7.19887C13.0603 7.39258 13.1143 7.48944 13.1985 7.55334C13.2827 7.61725 13.3875 7.64097 13.5972 7.68841L13.7881 7.73161C14.526 7.89857 14.895 7.98205 14.9828 8.26432C15.0706 8.54659 14.819 8.84072 14.316 9.42898L14.1858 9.58117C14.0429 9.74833 13.9714 9.83191 13.9392 9.93531C13.9071 10.0387 13.9179 10.1502 13.9395 10.3733L13.9592 10.5763C14.0352 11.3612 14.0733 11.7536 13.8435 11.9281C13.6136 12.1025 13.2682 11.9435 12.5773 11.6254L12.3986 11.5431C12.2022 11.4527 12.1041 11.4075 12 11.4075C11.8959 11.4075 11.7978 11.4527 11.6014 11.5431L11.4227 11.6254C10.7318 11.9435 10.3864 12.1025 10.1565 11.9281C9.92674 11.7536 9.96476 11.3612 10.0408 10.5763L10.0605 10.3733C10.0821 10.1502 10.0929 10.0387 10.0608 9.93531C10.0286 9.83191 9.95713 9.74833 9.81418 9.58117L9.68403 9.42898C9.18097 8.84072 8.92945 8.54659 9.01723 8.26432C9.10501 7.98205 9.47396 7.89857 10.2119 7.73161L10.4028 7.68841C10.6125 7.64097 10.7173 7.61725 10.8015 7.55334C10.8857 7.48944 10.9397 7.39258 11.0476 7.19887L11.1459 7.02251Z" stroke="currentColor" stroke-width="1.5"/>
<path d="M7.35111 15L6.71424 17.323C6.0859 19.6148 5.77173 20.7607 6.19097 21.3881C6.3379 21.6079 6.535 21.7844 6.76372 21.9008C7.41635 22.2331 8.42401 21.7081 10.4393 20.658C11.1099 20.3086 11.4452 20.1339 11.8014 20.0959C11.9335 20.0818 12.0665 20.0818 12.1986 20.0959C12.5548 20.1339 12.8901 20.3086 13.5607 20.658C15.576 21.7081 16.5837 22.2331 17.2363 21.9008C17.465 21.7844 17.6621 21.6079 17.809 21.3881C18.2283 20.7607 17.9141 19.6148 17.2858 17.323L16.6489 15" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<path d="M5.5 6.39691C5.17745 7.20159 5 8.08007 5 9C5 12.866 8.13401 16 12 16C15.866 16 19 12.866 19 9C19 5.13401 15.866 2 12 2C11.0801 2 10.2016 2.17745 9.39691 2.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.35111 15L6.71424 17.323C6.0859 19.6148 5.77173 20.7607 6.19097 21.3881C6.3379 21.6079 6.535 21.7844 6.76372 21.9008C7.41635 22.2331 8.42401 21.7081 10.4393 20.658C11.1099 20.3086 11.4452 20.1339 11.8014 20.0959C11.9335 20.0818 12.0665 20.0818 12.1986 20.0959C12.5548 20.1339 12.8901 20.3086 13.5607 20.658C15.576 21.7081 16.5837 22.2331 17.2363 21.9008C17.465 21.7844 17.6621 21.6079 17.809 21.3881C18.2283 20.7607 17.9141 19.6148 17.2858 17.323L16.6489 15" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<path d="M5.5 6.39691C5.17745 7.20159 5 8.08007 5 9C5 12.866 8.13401 16 12 16C15.866 16 19 12.866 19 9C19 5.13401 15.866 2 12 2C11.0801 2 10.2016 2.17745 9.39691 2.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1002 B

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.1459 7.02251C11.5259 6.34084 11.7159 6 12 6C12.2841 6 12.4741 6.34084 12.8541 7.02251L12.9524 7.19887C13.0603 7.39258 13.1143 7.48944 13.1985 7.55334C13.2827 7.61725 13.3875 7.64097 13.5972 7.68841L13.7881 7.73161C14.526 7.89857 14.895 7.98205 14.9828 8.26432C15.0706 8.54659 14.819 8.84072 14.316 9.42898L14.1858 9.58117C14.0429 9.74833 13.9714 9.83191 13.9392 9.93531C13.9071 10.0387 13.9179 10.1502 13.9395 10.3733L13.9592 10.5763C14.0352 11.3612 14.0733 11.7536 13.8435 11.9281C13.6136 12.1025 13.2682 11.9435 12.5773 11.6254L12.3986 11.5431C12.2022 11.4527 12.1041 11.4075 12 11.4075C11.8959 11.4075 11.7978 11.4527 11.6014 11.5431L11.4227 11.6254C10.7318 11.9435 10.3864 12.1025 10.1565 11.9281C9.92674 11.7536 9.96476 11.3612 10.0408 10.5763L10.0605 10.3733C10.0821 10.1502 10.0929 10.0387 10.0608 9.93531C10.0286 9.83191 9.95713 9.74833 9.81418 9.58117L9.68403 9.42898C9.18097 8.84072 8.92945 8.54659 9.01723 8.26432C9.10501 7.98205 9.47396 7.89857 10.2119 7.73161L10.4028 7.68841C10.6125 7.64097 10.7173 7.61725 10.8015 7.55334C10.8857 7.48944 10.9397 7.39258 11.0476 7.19887L11.1459 7.02251Z" stroke="currentColor" stroke-width="1.5"/>
<path d="M12 16.0678L8.22855 19.9728C7.68843 20.5321 7.41837 20.8117 7.18967 20.9084C6.66852 21.1289 6.09042 20.9402 5.81628 20.4602C5.69597 20.2495 5.65848 19.8695 5.5835 19.1095C5.54117 18.6804 5.52 18.4658 5.45575 18.2861C5.31191 17.8838 5.00966 17.5708 4.6211 17.4219C4.44754 17.3554 4.24033 17.3335 3.82589 17.2896C3.09187 17.212 2.72486 17.1732 2.52138 17.0486C2.05772 16.7648 1.87548 16.1662 2.08843 15.6266C2.18188 15.3898 2.45194 15.1102 2.99206 14.5509L5.45575 12" stroke="currentColor" stroke-width="1.5"/>
<path d="M12 16.0678L15.7715 19.9728C16.3116 20.5321 16.5816 20.8117 16.8103 20.9084C17.3315 21.1289 17.9096 20.9402 18.1837 20.4602C18.304 20.2495 18.3415 19.8695 18.4165 19.1095C18.4588 18.6804 18.48 18.4658 18.5442 18.2861C18.6881 17.8838 18.9903 17.5708 19.3789 17.4219C19.5525 17.3554 19.7597 17.3335 20.1741 17.2896C20.9081 17.212 21.2751 17.1732 21.4786 17.0486C21.9423 16.7648 22.1245 16.1662 21.9116 15.6266C21.8181 15.3898 21.5481 15.1102 21.0079 14.5509L18.5442 12" stroke="currentColor" stroke-width="1.5"/>
<path d="M5.5 6.39691C5.17745 7.20159 5 8.08007 5 9C5 12.866 8.13401 16 12 16C15.866 16 19 12.866 19 9C19 5.13401 15.866 2 12 2C11.0801 2 10.2016 2.17745 9.39691 2.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M17 8V6C17 4.11438 17 3.17157 16.4142 2.58579C15.8284 2 14.8856 2 13 2H11C9.11438 2 8.17157 2 7.58579 2.58579C7 3.17157 7 4.11438 7 6V8" stroke="currentColor" stroke-width="1.5"/>
<path d="M11.1459 12.0225C11.5259 11.3408 11.7159 11 12 11C12.2841 11 12.4741 11.3408 12.8541 12.0225L12.9524 12.1989C13.0603 12.3926 13.1143 12.4894 13.1985 12.5533C13.2827 12.6172 13.3875 12.641 13.5972 12.6884L13.7881 12.7316C14.526 12.8986 14.895 12.982 14.9828 13.2643C15.0706 13.5466 14.819 13.8407 14.316 14.429L14.1858 14.5812C14.0429 14.7483 13.9714 14.8319 13.9392 14.9353C13.9071 15.0387 13.9179 15.1502 13.9395 15.3733L13.9592 15.5763C14.0352 16.3612 14.0733 16.7536 13.8435 16.9281C13.6136 17.1025 13.2682 16.9435 12.5773 16.6254L12.3986 16.5431C12.2022 16.4527 12.1041 16.4075 12 16.4075C11.8959 16.4075 11.7978 16.4527 11.6014 16.5431L11.4227 16.6254C10.7318 16.9435 10.3864 17.1025 10.1565 16.9281C9.92674 16.7536 9.96476 16.3612 10.0408 15.5763L10.0605 15.3733C10.0821 15.1502 10.0929 15.0387 10.0608 14.9353C10.0286 14.8319 9.95713 14.7483 9.81418 14.5812L9.68403 14.429C9.18097 13.8407 8.92945 13.5466 9.01723 13.2643C9.10501 12.982 9.47396 12.8986 10.2119 12.7316L10.4028 12.6884C10.6125 12.641 10.7173 12.6172 10.8015 12.5533C10.8857 12.4894 10.9397 12.3926 11.0476 12.1989L11.1459 12.0225Z" stroke="currentColor" stroke-width="1.5"/>
<path d="M19.4286 16.975C19.7972 16.0553 20 15.0513 20 14C20 9.58172 16.4183 6 12 6C7.58172 6 4 9.58172 4 14C4 18.4183 7.58172 22 12 22C13.0513 22 14.0553 21.7972 14.975 21.4286" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M17 6C17 4.11438 17 3.17157 16.4142 2.58579C15.8284 2 14.8856 2 13 2H11C9.11438 2 8.17157 2 7.58579 2.58579C7 3.17157 7 4.11438 7 6" stroke="currentColor" stroke-width="1.5"/>
<path d="M11.1459 11.0225C11.5259 10.3408 11.7159 10 12 10C12.2841 10 12.4741 10.3408 12.8541 11.0225L12.9524 11.1989C13.0603 11.3926 13.1143 11.4894 13.1985 11.5533C13.2827 11.6172 13.3875 11.641 13.5972 11.6884L13.7881 11.7316C14.526 11.8986 14.895 11.982 14.9828 12.2643C15.0706 12.5466 14.819 12.8407 14.316 13.429L14.1858 13.5812C14.0429 13.7483 13.9714 13.8319 13.9392 13.9353C13.9071 14.0387 13.9179 14.1502 13.9395 14.3733L13.9592 14.5763C14.0352 15.3612 14.0733 15.7536 13.8435 15.9281C13.6136 16.1025 13.2682 15.9435 12.5773 15.6254L12.3986 15.5431C12.2022 15.4527 12.1041 15.4075 12 15.4075C11.8959 15.4075 11.7978 15.4527 11.6014 15.5431L11.4227 15.6254C10.7318 15.9435 10.3864 16.1025 10.1565 15.9281C9.92674 15.7536 9.96476 15.3612 10.0408 14.5763L10.0605 14.3733C10.0821 14.1502 10.0929 14.0387 10.0608 13.9353C10.0286 13.8319 9.95713 13.7483 9.81418 13.5812L9.68403 13.429C9.18097 12.8407 8.92945 12.5466 9.01723 12.2643C9.10501 11.982 9.47396 11.8986 10.2119 11.7316L10.4028 11.6884C10.6125 11.641 10.7173 11.6172 10.8015 11.5533C10.8857 11.4894 10.9397 11.3926 11.0476 11.1989L11.1459 11.0225Z" stroke="currentColor" stroke-width="1.5"/>
<path d="M15.5777 20.2111C13.8221 21.089 12.9443 21.5279 12 21.5279C11.0557 21.5279 10.1779 21.089 8.42229 20.2111C6.27063 19.1353 5.19479 18.5974 4.5974 17.6308C4 16.6642 4 15.4614 4 13.0557V12C4 9.17157 4 7.75736 4.87868 6.87868C5.75736 6 7.17157 6 10 6H14C16.8284 6 18.2426 6 19.1213 6.87868C20 7.75736 20 9.17157 20 12V13.0557C20 15.4614 20 16.6642 19.4026 17.6308C19.2876 17.8169 19.1548 17.9872 19 18.1484" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M17 8V6C17 4.11438 17 3.17157 16.4142 2.58579C15.8284 2 14.8856 2 13 2H11C9.11438 2 8.17157 2 7.58579 2.58579C7 3.17157 7 4.11438 7 6V8" stroke="currentColor" stroke-width="1.5"/>
<path d="M19.7943 11.0312C19.7943 9.93319 19.1944 8.92292 18.2305 8.39728L13.4362 5.78311C12.541 5.29495 11.4591 5.29495 10.5638 5.78311L5.76962 8.39728C4.80563 8.92292 4.20581 9.93318 4.20581 11.0312V15.9688C4.20581 17.0668 4.80563 18.0771 5.76962 18.6027L10.5638 21.2169C11.4591 21.705 12.541 21.705 13.4362 21.2169L18.2305 18.6027C19.1944 18.0771 19.7943 17.0668 19.7943 15.9688V15" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<path d="M11.1459 11.5228C11.5259 10.8411 11.7159 10.5002 12 10.5002C12.2841 10.5002 12.4741 10.8411 12.8541 11.5228L12.9524 11.6991C13.0603 11.8928 13.1143 11.9897 13.1985 12.0536C13.2827 12.1175 13.3875 12.1412 13.5972 12.1887L13.7881 12.2319C14.526 12.3988 14.895 12.4823 14.9828 12.7646C15.0706 13.0468 14.819 13.341 14.316 13.9292L14.1858 14.0814C14.0429 14.2486 13.9714 14.3322 13.9392 14.4356C13.9071 14.539 13.9179 14.6505 13.9395 14.8735L13.9592 15.0766C14.0352 15.8614 14.0733 16.2539 13.8435 16.4283C13.6136 16.6028 13.2682 16.4437 12.5773 16.1256L12.3986 16.0433C12.2022 15.9529 12.1041 15.9077 12 15.9077C11.8959 15.9077 11.7978 15.9529 11.6014 16.0433L11.4227 16.1256C10.7318 16.4437 10.3864 16.6028 10.1565 16.4283C9.92674 16.2539 9.96476 15.8614 10.0408 15.0766L10.0605 14.8735C10.0821 14.6505 10.0929 14.539 10.0608 14.4356C10.0286 14.3322 9.95713 14.2486 9.81418 14.0814L9.68403 13.9292C9.18097 13.341 8.92945 13.0468 9.01723 12.7646C9.10501 12.4823 9.47396 12.3988 10.2119 12.2319L10.4028 12.1887C10.6125 12.1412 10.7173 12.1175 10.8015 12.0536C10.8857 11.9897 10.9397 11.8928 11.0476 11.6991L11.1459 11.5228Z" stroke="currentColor" stroke-width="1.5"/>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

4
public/icons/moon.svg Normal file
View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M21.0672 11.8568L20.4253 11.469L21.0672 11.8568ZM12.1432 2.93276L11.7553 2.29085V2.29085L12.1432 2.93276ZM7.37554 20.013C7.017 19.8056 6.5582 19.9281 6.3508 20.2866C6.14339 20.6452 6.26591 21.104 6.62446 21.3114L7.37554 20.013ZM2.68862 17.3755C2.89602 17.7341 3.35482 17.8566 3.71337 17.6492C4.07191 17.4418 4.19443 16.983 3.98703 16.6245L2.68862 17.3755ZM21.25 12C21.25 17.1086 17.1086 21.25 12 21.25V22.75C17.9371 22.75 22.75 17.9371 22.75 12H21.25ZM2.75 12C2.75 6.89137 6.89137 2.75 12 2.75V1.25C6.06294 1.25 1.25 6.06294 1.25 12H2.75ZM15.5 14.25C12.3244 14.25 9.75 11.6756 9.75 8.5H8.25C8.25 12.5041 11.4959 15.75 15.5 15.75V14.25ZM20.4253 11.469C19.4172 13.1373 17.5882 14.25 15.5 14.25V15.75C18.1349 15.75 20.4407 14.3439 21.7092 12.2447L20.4253 11.469ZM9.75 8.5C9.75 6.41182 10.8627 4.5828 12.531 3.57467L11.7553 2.29085C9.65609 3.5593 8.25 5.86509 8.25 8.5H9.75ZM12 2.75C11.9115 2.75 11.8077 2.71008 11.7324 2.63168C11.6686 2.56527 11.6538 2.50244 11.6503 2.47703C11.6461 2.44587 11.6482 2.35557 11.7553 2.29085L12.531 3.57467C13.0342 3.27065 13.196 2.71398 13.1368 2.27627C13.0754 1.82126 12.7166 1.25 12 1.25V2.75ZM21.7092 12.2447C21.6444 12.3518 21.5541 12.3539 21.523 12.3497C21.4976 12.3462 21.4347 12.3314 21.3683 12.2676C21.2899 12.1923 21.25 12.0885 21.25 12H22.75C22.75 11.2834 22.1787 10.9246 21.7237 10.8632C21.286 10.804 20.7293 10.9658 20.4253 11.469L21.7092 12.2447ZM12 21.25C10.3139 21.25 8.73533 20.7996 7.37554 20.013L6.62446 21.3114C8.2064 22.2265 10.0432 22.75 12 22.75V21.25ZM3.98703 16.6245C3.20043 15.2647 2.75 13.6861 2.75 12H1.25C1.25 13.9568 1.77351 15.7936 2.68862 17.3755L3.98703 16.6245Z" fill="currentColor"/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.5 12.5L10.5 14.5L15.5 9.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M22 12C22 16.714 22 19.0711 20.5355 20.5355C19.0711 22 16.714 22 12 22C7.28595 22 4.92893 22 3.46447 20.5355C2 19.0711 2 16.714 2 12C2 7.28595 2 4.92893 3.46447 3.46447C4.92893 2 7.28595 2 12 2C16.714 2 19.0711 2 20.5355 3.46447C21.5093 4.43821 21.8356 5.80655 21.9449 8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 709 B

4
public/icons/record.svg Normal file
View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7 3.33782C8.47087 2.48697 10.1786 2 12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 10.1786 2.48697 8.47087 3.33782 7" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 467 B

7
public/icons/running.svg Normal file
View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="18.5" cy="4.5" r="2.5" stroke="currentColor" stroke-width="1.5"/>
<path d="M9 17L7.99923 18.2009C7.262 19.0856 6.89338 19.5279 6.38945 19.764C5.88552 20 5.30973 20 4.15813 20H3" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<path d="M5.80619 9.47232C6.16633 9.2677 6.2924 8.80986 6.08777 8.44972C5.88315 8.08958 5.42531 7.96351 5.06517 8.16814L5.80619 9.47232ZM3.62949 8.98386C3.26935 9.18849 3.14328 9.64632 3.34791 10.0065C3.55253 10.3666 4.01037 10.4927 4.37051 10.288L3.62949 8.98386ZM15.7502 10.125L15.1262 10.541L15.1262 10.541L15.7502 10.125ZM15.8125 10.2185L16.4366 9.80242L16.4366 9.80242L15.8125 10.2185ZM10.9688 6.65329L11.0462 5.90729L10.9688 6.65329ZM10.7398 6.63589L10.7065 7.38516H10.7065L10.7398 6.63589ZM10.1548 6.67319L10.0522 5.93024L10.0522 5.93024L10.1548 6.67319ZM21 12.7496C21.4142 12.7496 21.75 12.4138 21.75 11.9996C21.75 11.5854 21.4142 11.2496 21 11.2496V12.7496ZM8.73808 6.27249C8.34994 6.41715 8.15257 6.84906 8.29722 7.23719C8.44188 7.62533 8.87379 7.8227 9.26192 7.67805L8.73808 6.27249ZM5.06517 8.16814L3.62949 8.98386L4.37051 10.288L5.80619 9.47232L5.06517 8.16814ZM15.1262 10.541L15.1885 10.6345L16.4366 9.80242L16.3742 9.70894L15.1262 10.541ZM11.0462 5.90729C10.9438 5.89666 10.853 5.89018 10.773 5.88663L10.7065 7.38516C10.7559 7.38734 10.8172 7.39158 10.8914 7.39928L11.0462 5.90729ZM19.1407 12.7496H21V11.2496H19.1407V12.7496ZM10.773 5.88663C10.5099 5.87497 10.2617 5.90131 10.0522 5.93024L10.2574 7.41614C10.4355 7.39155 10.5786 7.37949 10.7065 7.38516L10.773 5.88663ZM15.1885 10.6345C16.0695 11.9559 17.5525 12.7496 19.1407 12.7496V11.2496C18.0541 11.2496 17.0393 10.7065 16.4366 9.80242L15.1885 10.6345ZM16.3742 9.70894C15.1968 7.94284 13.4289 6.15461 11.0462 5.90729L10.8914 7.39928C12.5939 7.576 14.0254 8.88987 15.1262 10.541L16.3742 9.70894ZM9.26192 7.67805C9.62279 7.54355 9.94386 7.45943 10.2574 7.41614L10.0522 5.93024C9.61073 5.9912 9.18212 6.107 8.73808 6.27249L9.26192 7.67805Z" fill="currentColor"/>
<path d="M14.0001 8.5L11.7793 11.2756C10.9429 12.321 10.5246 12.8438 10.4579 13.413C10.4204 13.733 10.4608 14.0573 10.5756 14.3584C10.7798 14.8939 11.3134 15.2981 12.3807 16.1066C13.1936 16.7225 13.6 17.0304 13.8755 17.4329C14.0326 17.6625 14.157 17.9129 14.2452 18.1767C14.3997 18.6394 14.3997 19.1493 14.3997 20.1692V21.9998" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9 17C9.85038 16.3697 10.8846 16 12 16C13.1154 16 14.1496 16.3697 15 17" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<ellipse cx="15" cy="10.5" rx="1" ry="1.5" fill="currentColor"/>
<ellipse cx="9" cy="10.5" rx="1" ry="1.5" fill="currentColor"/>
<path d="M7 3.33782C8.47087 2.48697 10.1786 2 12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 10.1786 2.48697 8.47087 3.33782 7" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 747 B

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9 17C9.85038 16.3697 10.8846 16 12 16C13.1154 16 14.1496 16.3697 15 17" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<ellipse cx="15" cy="10.5" rx="1" ry="1.5" fill="currentColor"/>
<ellipse cx="9" cy="10.5" rx="1" ry="1.5" fill="currentColor"/>
<path d="M22 12C22 16.714 22 19.0711 20.5355 20.5355C19.0711 22 16.714 22 12 22C7.28595 22 4.92893 22 3.46447 20.5355C2 19.0711 2 16.714 2 12C2 7.28595 2 4.92893 3.46447 3.46447C4.92893 2 7.28595 2 12 2C16.714 2 19.0711 2 20.5355 3.46447C21.5093 4.43821 21.8356 5.80655 21.9449 8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 859 B

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7 12L14 12M14 12L11 15M14 12L11 9" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M17 16L17 8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<path d="M22 12C22 16.714 22 19.0711 20.5355 20.5355C19.0711 22 16.714 22 12 22C7.28595 22 4.92893 22 3.46447 20.5355C2 19.0711 2 16.714 2 12C2 7.28595 2 4.92893 3.46447 3.46447C4.92893 2 7.28595 2 12 2C16.714 2 19.0711 2 20.5355 3.46447C21.5093 4.43821 21.8356 5.80655 21.9449 8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 804 B

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9 16C9.85038 16.6303 10.8846 17 12 17C13.1154 17 14.1496 16.6303 15 16" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<ellipse cx="15" cy="10.5" rx="1" ry="1.5" fill="currentColor"/>
<ellipse cx="9" cy="10.5" rx="1" ry="1.5" fill="currentColor"/>
<path d="M22 14C22 17.7712 22 19.6569 20.8284 20.8284C19.6569 22 17.7712 22 14 22" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<path d="M10 22C6.22876 22 4.34315 22 3.17157 20.8284C2 19.6569 2 17.7712 2 14" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<path d="M10 2C6.22876 2 4.34315 2 3.17157 3.17157C2 4.34315 2 6.22876 2 10" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<path d="M14 2C17.7712 2 19.6569 2 20.8284 3.17157C22 4.34315 22 6.22876 22 10" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

12
public/icons/sun.svg Normal file
View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.28451 10.3333C7.10026 10.8546 7 11.4156 7 12C7 14.7614 9.23858 17 12 17C14.7614 17 17 14.7614 17 12C17 9.23858 14.7614 7 12 7C11.4156 7 10.8546 7.10026 10.3333 7.28451" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<path d="M12 2V4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<path d="M12 20V22" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<path d="M4 12L2 12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<path d="M22 12L20 12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<path d="M19.7778 4.22266L17.5558 6.25424" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<path d="M4.22217 4.22266L6.44418 6.25424" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<path d="M6.44434 17.5557L4.22211 19.7779" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<path d="M19.7778 19.7773L17.5558 17.5551" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

4
public/icons/unread.svg Normal file
View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7 12.9L10.1429 16.5L12.1071 14.25M18 7.5L14.0714 12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 385 B

View File

@@ -0,0 +1,70 @@
<script>
import InlineSvg from './InlineSvg.svelte';
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
export let achievementCount = { unlocked: 0, total: 0 };
function handleClick() {
dispatch('click');
}
</script>
<button
class="achievement-button"
on:click={handleClick}
title="View achievements"
>
<span class="qh-value">
<div class="achievement-icon">
<InlineSvg path="/icons/medal-ribbon.svg" alt="Achievements" />
</div>
<span class="achievement-count">{achievementCount.unlocked}/{achievementCount.total}</span>
</span>
</button>
<style>
.achievement-button {
background: transparent;
border: none;
border-radius: 0;
padding: 0;
display: flex;
align-items: center;
cursor: pointer;
color: var(--color-text);
font-size: 0.9rem;
transition: all 0.2s ease;
min-height: 23px;
}
.achievement-button:hover {
color: var(--color-primary);
}
.achievement-button .qh-label {
color: var(--color-text-secondary);
margin-right: 0.35rem;
}
.achievement-button .qh-value {
font-weight: 600;
display: flex;
align-items: center;
gap: 0.35rem;
}
.achievement-icon {
width: 1rem;
height: 1rem;
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
}
.achievement-count {
color: inherit;
}
</style>

View File

@@ -0,0 +1,458 @@
<script>
import { createEventDispatcher } from 'svelte';
import InlineSvg from './InlineSvg.svelte';
const dispatch = createEventDispatcher();
// Props
export let gameStats = { correct: 0, wrong: 0, total: 0, skipped: 0 };
export let currentStreak = 0;
export let show = false;
// Achievement state
let achievements = {};
let newAchievements = [];
// Achievement definitions
const achievementDefinitions = {
'first_correct': {
name: 'First Victory',
description: 'Answer your first question correctly',
icon: 'smile-squre.svg',
requirement: () => gameStats.correct >= 1
},
'perfect_10': {
name: 'Perfect Ten',
description: 'Answer 10 questions correctly without any mistakes',
icon: 'medal-star.svg',
requirement: () => currentStreak >= 10
},
'speedrunner': {
name: 'Speed Runner',
description: 'Skip 10 questions in a row',
icon: 'running.svg',
requirement: () => achievements.consecutive_skips >= 10
},
'explorer': {
name: 'World Explorer',
description: 'Answer 50 questions correctly',
icon: 'crown-minimalistic.svg',
requirement: () => gameStats.correct >= 50
},
'master': {
name: 'Flag Master',
description: 'Answer 100 questions correctly',
icon: 'cup-first.svg',
requirement: () => gameStats.correct >= 100
},
'persistent': {
name: 'Persistent Scholar',
description: 'Answer 25 questions (correct or wrong)',
icon: 'medal-ribbons-star.svg',
requirement: () => gameStats.total >= 25
},
'perfectionist': {
name: 'Perfectionist',
description: 'Achieve 90% accuracy with at least 20 answers',
icon: 'medal-star-circle.svg',
requirement: () => gameStats.total >= 20 && (gameStats.correct / gameStats.total) >= 0.9
},
'party_time': {
name: 'Party Time!',
description: 'Answer 5 questions correctly in a row',
icon: 'confetti-minimalistic.svg',
requirement: () => currentStreak >= 5
},
'dedication': {
name: 'Dedicated Learner',
description: 'Answer 10 questions in total',
icon: 'check-circle.svg',
requirement: () => gameStats.total >= 10
},
'legend': {
name: 'Geography Legend',
description: 'Answer 200 questions correctly',
icon: 'crown-minimalistic.svg',
requirement: () => gameStats.correct >= 200
}
};
// Achievement functions
export function loadAchievements() {
try {
const saved = localStorage.getItem('flagQuizAchievements');
if (saved) {
achievements = JSON.parse(saved);
} else {
achievements = { consecutive_skips: 0 };
}
} catch (error) {
console.error('Error loading achievements:', error);
achievements = { consecutive_skips: 0 };
}
}
function saveAchievements() {
localStorage.setItem('flagQuizAchievements', JSON.stringify(achievements));
}
export function checkAchievements() {
const newUnlocked = [];
Object.entries(achievementDefinitions).forEach(([key, achievement]) => {
if (!achievements[key] && achievement.requirement()) {
achievements[key] = {
unlocked: true,
unlockedAt: Date.now()
};
newUnlocked.push({
key,
...achievement
});
}
});
if (newUnlocked.length > 0) {
newAchievements = [...newAchievements, ...newUnlocked];
saveAchievements();
showNewAchievements();
dispatch('achievementsUnlocked', newUnlocked);
}
}
function showNewAchievements() {
// Show achievement notifications briefly
setTimeout(() => {
newAchievements = [];
}, 5000);
}
export function getUnlockedAchievements() {
return Object.entries(achievements)
.filter(([key, data]) => data.unlocked && achievementDefinitions[key])
.map(([key, data]) => ({
key,
...achievementDefinitions[key],
...data
}));
}
export function getAchievementCount() {
return {
unlocked: Object.values(achievements).filter(a => a.unlocked).length,
total: Object.keys(achievementDefinitions).length
};
}
export function incrementConsecutiveSkips() {
achievements.consecutive_skips = (achievements.consecutive_skips || 0) + 1;
saveAchievements();
}
export function resetConsecutiveSkips() {
achievements.consecutive_skips = 0;
saveAchievements();
}
// Initialize achievements on component mount
loadAchievements();
function handleOverlayClick(event) {
if (event.target === event.currentTarget) {
show = false;
dispatch('close');
}
}
function handleOverlayKeydown(event) {
if (event.key === 'Escape') {
show = false;
dispatch('close');
}
}
function closeModal() {
show = false;
dispatch('close');
}
</script>
<!-- Achievement display modal -->
{#if show}
<div
class="achievements-overlay"
on:click={handleOverlayClick}
tabindex="0"
role="button"
aria-label="Close achievements (click background or press Escape)"
on:keydown={handleOverlayKeydown}
>
<div
class="achievements-modal"
role="dialog"
aria-modal="true"
aria-labelledby="achievements-title"
>
<div class="achievements-header">
<h2 id="achievements-title">🏆 Achievements</h2>
<button class="close-btn" on:click={closeModal}>✕</button>
</div>
<div class="achievements-content">
{#each Object.entries(achievementDefinitions) as [key, def]}
<div class="achievement-item" class:unlocked={achievements[key]?.unlocked}>
<div class="achievement-icon">
<InlineSvg path={`/icons/${def.icon}`} alt={def.name} />
</div>
<div class="achievement-info">
<div class="achievement-name">{def.name}</div>
<div class="achievement-description">{def.description}</div>
{#if achievements[key]?.unlocked}
<div class="achievement-unlocked">
<div class="status-icon">
<InlineSvg path="/icons/check-circle.svg" alt="Unlocked" />
</div>
<span>Unlocked!</span>
</div>
{:else}
<div class="achievement-locked">
<div class="status-icon">
<InlineSvg path="/icons/lock-locked.svg" alt="Locked" />
</div>
<span>Locked</span>
</div>
{/if}
</div>
</div>
{/each}
</div>
</div>
</div>
{/if}
<!-- Achievement notifications -->
{#if newAchievements.length > 0}
<div class="achievement-notifications">
{#each newAchievements as achievement}
<div class="achievement-notification" key={achievement.key}>
<div class="notification-icon">
<InlineSvg path={`/icons/${achievement.icon}`} alt="Achievement" />
</div>
<div class="notification-text">
<div class="notification-title">Achievement Unlocked!</div>
<div class="notification-name">{achievement.name}</div>
</div>
</div>
{/each}
</div>
{/if}
<style>
/* Achievement modal styles */
.achievements-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.achievements-modal {
background: var(--color-bg-secondary);
border: 2px solid var(--color-border);
border-radius: 1rem;
padding: 0;
max-width: 600px;
width: 90%;
max-height: 80vh;
overflow-y: auto;
box-shadow: 0 10px 24px rgba(0,0,0,0.25);
}
.achievements-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1.5rem;
border-bottom: 1px solid var(--color-border);
position: sticky;
top: 0;
background: var(--color-bg-secondary);
z-index: 1;
}
.achievements-header h2 {
margin: 0;
font-size: 1.5rem;
color: var(--color-text-primary);
}
.close-btn {
background: none;
border: none;
font-size: 1.5rem;
cursor: pointer;
color: var(--color-text-primary);
padding: 0.25rem;
border-radius: 0.25rem;
transition: background-color 0.3s ease;
}
.close-btn:hover {
background-color: var(--color-border);
}
.achievements-content {
padding: 1.5rem;
display: flex;
flex-direction: column;
gap: 1rem;
}
.achievement-item {
display: flex;
align-items: center;
gap: 1rem;
padding: 1rem;
background: var(--color-bg-primary);
border: 1px solid var(--color-border);
border-radius: 0.5rem;
transition: all 0.2s ease;
}
.achievement-item.unlocked {
border-color: var(--color-primary);
background: var(--color-primary-light);
}
.achievement-icon {
width: 48px;
height: 48px;
display: flex;
align-items: center;
justify-content: center;
}
.achievement-item.unlocked .achievement-icon {
color: #fbbf24;
}
.achievement-info {
flex: 1;
}
.achievement-name {
font-size: 1.1rem;
font-weight: 600;
color: var(--color-text-primary);
margin-bottom: 0.25rem;
}
.achievement-description {
font-size: 0.9rem;
color: var(--color-text-secondary);
margin-bottom: 0.5rem;
}
.achievement-unlocked {
font-size: 0.8rem;
color: #22c55e;
font-weight: 600;
display: flex;
align-items: center;
justify-content: end;
gap: 0.25rem;
}
.achievement-locked {
font-size: 0.8rem;
color: var(--color-text-secondary);
font-weight: 600;
display: flex;
align-items: center;
justify-content: end;
gap: 0.25rem;
}
.status-icon {
width: 16px;
height: 16px;
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
}
.achievement-unlocked .status-icon {
color: #22c55e;
}
.achievement-locked .status-icon {
color: var(--color-text-secondary);
}
/* Achievement notifications */
.achievement-notifications {
position: fixed;
top: 1rem;
right: 1rem;
z-index: 1100;
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.achievement-notification {
background: var(--color-primary);
color: white;
border-radius: 0.5rem;
padding: 1rem;
display: flex;
align-items: center;
gap: 0.75rem;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
animation: slideInRight 0.3s ease-out;
max-width: 300px;
}
.notification-icon {
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
background: rgba(255,255,255,0.2);
border-radius: 50%;
}
.notification-text {
flex: 1;
}
.notification-title {
font-size: 0.875rem;
font-weight: 600;
margin-bottom: 0.25rem;
}
.notification-name {
font-size: 0.75rem;
opacity: 0.9;
}
@keyframes slideInRight {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
</style>

View File

@@ -1,4 +1,5 @@
<script>
import InlineSvg from "./InlineSvg.svelte";
export let allLogos = [];
export let allTags = [];
export let selectedTags = [];
@@ -122,18 +123,9 @@
aria-label="Open filter options"
class:active={tagDropdownOpen}
>
<svg
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M3 4a1 1 0 0 1 1-1h16a1 1 0 0 1 1 1v2a1 1 0 0 1-.293.707L14 13.414V19a1 1 0 0 1-.553.894l-2 1A1 1 0 0 1 10 20v-6.586L3.293 6.707A1 1 0 0 1 3 6V4z"
fill="currentColor"
/>
</svg>
<span class="filter-icon">
<InlineSvg path="/icons/filter.svg" alt="Filter" />
</span>
{#if selectedTags.length + selectedBrands.length + selectedVariants.length + (compactMode ? 1 : 0) > 0}
<span class="filter-count"
>{selectedTags.length + selectedBrands.length + selectedVariants.length + (compactMode ? 1 : 0)}</span
@@ -142,6 +134,7 @@
</button>
{#if tagDropdownOpen}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div class="filter-dropdown-panel" on:click|stopPropagation>
<div class="filter-options">
<button
@@ -589,6 +582,12 @@
border-color: var(--color-accent);
}
.filter-icon {
width: 16px;
height: 16px;
display: inline-flex;
}
.filter-count {
background: var(--color-accent);
color: #fff;

View File

@@ -5,6 +5,8 @@
import ThemeSwitcher from "./ThemeSwitcher.svelte";
import ListViewSwitcher from "./ListViewSwitcher.svelte";
import SearchBar from "./SearchBar.svelte";
import InlineSvg from "./InlineSvg.svelte";
import AchievementButton from "./AchievementButton.svelte";
import { collections } from "../collections.js";
export let displayLogos = [];
@@ -37,6 +39,8 @@
// Quiz-only data
export let score = null;
export let gameStats = null;
export let achievementCount = { unlocked: 0, total: 0 };
export let onAchievementClick = () => {};
let dropdownOpen = false;
@@ -70,43 +74,47 @@
<header class="main-header">
<div class="header-row">
<div class="header-title">
<div class="header-icon">
<img src="favicon.svg" alt="Logo Gallery icon" />
<div class="header-left">
<div class="header-title">
<div class="header-icon">
<img src="favicon.svg" alt="Logo Gallery icon" />
</div>
<button class="collection-title-btn" on:click={handleTitleClick} aria-haspopup="listbox" aria-expanded={dropdownOpen}>
{displayLabel} <span class="triangle"></span>
</button>
{#if dropdownOpen}
<ul class="collection-dropdown" role="listbox">
{#each collections as c}
<li
class:active={c.name === collection}
role="option"
aria-selected={c.name === collection}
tabindex="0"
on:click={() => handleCollectionSelect(c.name)}
on:keydown={(e) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
handleCollectionSelect(c.name);
}
}}
>{c.title} Gallery</li>
{/each}
</ul>
{/if}
</div>
<button class="collection-title-btn" on:click={handleTitleClick} aria-haspopup="listbox" aria-expanded={dropdownOpen}>
{displayLabel} <span class="triangle"></span>
</button>
{#if dropdownOpen}
<ul class="collection-dropdown" role="listbox">
{#each collections as c}
<li
class:active={c.name === collection}
role="option"
aria-selected={c.name === collection}
tabindex="0"
on:click={() => handleCollectionSelect(c.name)}
on:keydown={(e) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
handleCollectionSelect(c.name);
}
}}
>{c.title} Gallery</li>
{/each}
</ul>
{#if !isGameMode}
<span class="logo-count">
{#if (searchQuery && searchQuery.trim() !== "") || selectedTags.length > 0 || selectedBrands.length > 0 || selectedVariants.length > 0 || compactMode}
{displayLogos ? displayLogos.length : 0} of {allLogos ? allLogos.length : 0} images
displayed
{:else}
{allLogos ? allLogos.length : 0} images in gallery
{/if}
</span>
{/if}
</div>
<span class="logo-count">
{#if (searchQuery && searchQuery.trim() !== "") || selectedTags.length > 0 || selectedBrands.length > 0 || selectedVariants.length > 0 || compactMode}
{displayLogos ? displayLogos.length : 0} of {allLogos ? allLogos.length : 0} images
displayed
{:else}
{allLogos ? allLogos.length : 0} images in gallery
{/if}
</span>
<a href="#/game" class="game-button" class:active={isGameMode} title={isGameMode ? "Back to Main" : "Quiz Games"} on:click|preventDefault={handleGameClick}>
🎮
<span class="icon"><InlineSvg path="/icons/gamepad.svg" alt="Games" /></span>
</a>
<ThemeSwitcher {theme} {setTheme} />
</div>
@@ -117,40 +125,50 @@
<div class="quiz-header-stats">
<div class="qh-block">
<span class="qh-label">Current:</span>
<span class="qh-value">{score ? `${score.correct}/${score.total}` : '0/0'} {score && score.skipped > 0 ? `(⏭️ ${score.skipped})` : ''}</span>
<span class="qh-value">{score ? `${score.correct}/${score.total}` : '0/0'} {score && score.skipped > 0 ? `(` : ''}{#if score && score.skipped > 0}<span class="quiz-icon quiz-skip"><InlineSvg path="/icons/skip-square.svg" alt="Skipped" /></span> {score.skipped}){/if}</span>
</div>
<div class="qh-block">
<span class="qh-label">All Time:</span>
<span class="qh-value">{gameStats?.correct || 0} {gameStats?.wrong || 0} {gameStats?.skipped > 0 ? `⏭️ ${gameStats.skipped}` : ''}</span>
<span class="qh-value"><span class="quiz-icon quiz-ok"><InlineSvg path="/icons/ok-square.svg" alt="Correct" /></span> {gameStats?.correct || 0} <span class="quiz-icon quiz-fail"><InlineSvg path="/icons/fail-square.svg" alt="Wrong" /></span> {gameStats?.wrong || 0} {gameStats?.skipped > 0 ? `` : ''}{#if gameStats?.skipped > 0}<span class="quiz-icon quiz-skip"><InlineSvg path="/icons/skip-square.svg" alt="Skipped" /></span> {gameStats.skipped}{/if}</span>
</div>
<div class="qh-block achievement-block">
<AchievementButton
{achievementCount}
on:click={onAchievementClick}
/>
</div>
</div>
{:else}
<SearchBar {searchQuery} {setSearchQuery} />
<Filter
{allLogos}
{allTags}
{selectedTags}
{selectedBrands}
{selectedVariants}
{tagDropdownOpen}
{toggleDropdown}
{addTag}
{removeTag}
{addBrand}
{removeBrand}
{addVariant}
{removeVariant}
{getTagObj}
{compactMode}
{setCompactMode}
collection={currentCollectionObj}
/>
<ListViewSwitcher
{viewMode}
{setGridView}
{setListView}
{setCompactView}
/>
<div class="header-controls-left">
<SearchBar {searchQuery} {setSearchQuery} />
<Filter
{allLogos}
{allTags}
{selectedTags}
{selectedBrands}
{selectedVariants}
{tagDropdownOpen}
{toggleDropdown}
{addTag}
{removeTag}
{addBrand}
{removeBrand}
{addVariant}
{removeVariant}
{getTagObj}
{compactMode}
{setCompactMode}
collection={currentCollectionObj}
/>
</div>
<div class="header-controls-right">
<ListViewSwitcher
{viewMode}
{setGridView}
{setListView}
{setCompactView}
/>
</div>
{/if}
</div>
{/if}
@@ -166,10 +184,25 @@
}
.header-row {
display: grid;
grid-template-columns: 1fr auto 1fr;
grid-template-areas: "left center right";
align-items: center;
gap: 1rem;
}
.header-left {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1.5rem;
gap: 1rem;
grid-area: left;
justify-self: start;
}
.header-title {
display: flex;
align-items: center;
gap: 0.5em;
}
.logo-count {
@@ -178,9 +211,8 @@
font-weight: normal;
color: var(--color-text);
opacity: 0.7;
margin-left: 1rem;
align-self: center;
padding-top: 0.9rem;
white-space: nowrap;
}
.game-button {
@@ -195,20 +227,21 @@
text-decoration: none;
font-size: 1.2rem;
transition: all 0.3s ease;
margin-left: 0.5rem;
cursor: pointer;
color: var(--color-text);
grid-area: center;
justify-self: center;
}
.game-button:hover {
background: var(--color-primary);
border-color: var(--color-primary);
transform: translateY(-1px);
}
.game-button.active {
background: var(--color-primary);
border-color: var(--color-primary);
color: white;
color: white;
}
.game-button.active:hover {
@@ -216,10 +249,24 @@
border-color: var(--color-primary-dark, #0056b3);
}
.game-button .icon {
width: 22px;
height: 22px;
display: inline-flex;
}
.main-header :global(.theme-switcher) {
grid-area: right;
justify-self: end;
margin-left: 0;
}
.header-title {
display: flex;
align-items: center;
gap: 0.5em;
grid-area: left;
justify-self: start;
}
@@ -288,6 +335,7 @@
.header-controls {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
margin: 1rem 0;
}
@@ -295,6 +343,18 @@
justify-content: center;
}
.header-controls-left {
display: flex;
align-items: center;
gap: 1rem;
}
.header-controls-right {
display: flex;
align-items: center;
gap: 1rem;
}
.quiz-header-stats {
display: flex;
align-items: center;
@@ -319,35 +379,50 @@
font-weight: 600;
}
.quiz-icon {
display: inline-flex;
width: 20px;
height: 20px;
vertical-align: middle;
margin-right: 0.25rem;
}
.quiz-icon.quiz-ok {
color: #22c55e; /* green */
}
.quiz-icon.quiz-fail {
color: #ef4444; /* red */
}
.quiz-icon.quiz-skip {
color: #6b7280; /* gray */
}
.achievement-block {
margin-left: auto;
}
@media (max-width: 700px) {
.header-row {
display: grid;
grid-template-columns: 1fr auto auto auto;
grid-template-columns: 1fr auto auto;
grid-template-rows: auto auto;
grid-template-areas:
"left center right"
"count count count";
gap: 0.5rem;
align-items: center;
}
.header-title {
grid-column: 1;
grid-row: 1;
}
.game-button {
grid-column: 2;
grid-row: 1;
margin-left: 0;
.header-left {
flex-direction: column;
align-items: flex-start;
gap: 0.5rem;
}
.logo-count {
grid-column: 1 / -1;
grid-row: 2;
margin-left: 0;
padding-top: 0;
grid-area: count;
justify-self: start;
}
.header-controls {
} .header-controls {
display: grid;
grid-template-columns: 1fr auto;
grid-template-rows: auto auto;
@@ -355,21 +430,20 @@
margin: 1rem 0;
}
.header-controls :global(.search-bar) {
.header-controls-left {
grid-column: 1;
grid-row: 1;
grid-row: 1 / -1;
display: grid;
grid-template-columns: 1fr;
grid-template-rows: auto auto;
gap: 1rem;
}
.header-controls :global(.view-toggle) {
.header-controls-right {
grid-column: 2;
grid-row: 1;
}
.header-controls :global(.filter-section) {
grid-column: 1 / -1;
grid-row: 2;
}
/* Prevent header title from wrapping and reduce size on small screens */
.collection-title-btn {
font-size: 1.1rem;

View File

@@ -1,4 +1,5 @@
<script>
import InlineSvg from "./InlineSvg.svelte";
export let theme = "system";
export let setTheme = () => {};
</script>
@@ -10,66 +11,21 @@
class:active={theme === "system"}
aria-label="System theme"
>
<svg
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
><circle
cx="10"
cy="10"
r="8"
stroke="currentColor"
stroke-width="2"
/><path
d="M10 2a8 8 0 0 1 8 8"
stroke="currentColor"
stroke-width="2"
/></svg
>
<span class="icon"><InlineSvg path="/icons/record.svg" alt="System theme" /></span>
</button>
<button
on:click={() => setTheme("light")}
class:active={theme === "light"}
aria-label="Light mode"
>
<svg
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
><circle
cx="10"
cy="10"
r="5"
stroke="currentColor"
stroke-width="2"
/><path
d="M10 1v2M10 17v2M3.22 3.22l1.42 1.42M15.36 15.36l1.42 1.42M1 10h2M17 10h2M3.22 16.78l1.42-1.42M15.36 4.64l1.42-1.42"
stroke="currentColor"
stroke-width="2"
/></svg
>
<span class="icon"><InlineSvg path="/icons/sun.svg" alt="Light mode" /></span>
</button>
<button
on:click={() => setTheme("dark")}
class:active={theme === "dark"}
aria-label="Dark mode"
>
<svg
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
><path
d="M15.5 13A7 7 0 0 1 7 4.5a7 7 0 1 0 8.5 8.5z"
stroke="currentColor"
stroke-width="2"
/></svg
>
<span class="icon"><InlineSvg path="/icons/moon.svg" alt="Dark mode" /></span>
</button>
</div>
</div>
@@ -81,4 +37,10 @@
gap: 0.2rem;
margin-left: auto;
}
.theme-switch-group .icon {
width: 20px;
height: 20px;
display: inline-flex;
}
</style>

View File

@@ -1,6 +1,9 @@
<script>
import { onMount } from 'svelte';
import Header from '../components/Header.svelte';
import InlineSvg from '../components/InlineSvg.svelte';
import Achievements from '../components/Achievements.svelte';
import AchievementButton from '../components/AchievementButton.svelte';
// Game data
let flags = [];
@@ -20,15 +23,24 @@
let resultMessage = '';
let showResult = false;
let timeoutId = null;
let showCountryInfo = false;
let showResultCountryInfo = false;
// Scoring
let score = { correct: 0, total: 0, skipped: 0 };
let gameStats = { correct: 0, wrong: 0, total: 0, skipped: 0 };
// Achievement System
let currentStreak = 0;
let showAchievements = false;
let achievementsComponent;
let achievementCount = { unlocked: 0, total: 0 };
// Settings
let autoAdvance = true;
let showSettings = false;
let settingsLoaded = false;
let showResetConfirmation = false;
// Theme
let theme = 'system';
@@ -44,6 +56,11 @@
localStorage.setItem('flagQuizSettings', JSON.stringify({ autoAdvance }));
}
// Update achievement count when achievements component is available
$: if (achievementsComponent) {
updateAchievementCount();
}
// Load game stats from localStorage
onMount(async () => {
// Initialize theme
@@ -146,6 +163,8 @@
selectedAnswer = null;
correctAnswer = null;
answered = false;
showCountryInfo = false;
showResultCountryInfo = false;
// Randomly choose question type
questionType = Math.random() < 0.5 ? 'flag-to-country' : 'country-to-flag';
@@ -204,14 +223,28 @@
if (isCorrect) {
score.correct++;
gameStats.correct++;
currentStreak++;
// Reset consecutive skips on correct answer
if (achievementsComponent) {
achievementsComponent.resetConsecutiveSkips();
}
} else {
gameStats.wrong++;
currentStreak = 0; // Reset streak on wrong answer
if (achievementsComponent) {
achievementsComponent.resetConsecutiveSkips();
}
}
gameStats.total++;
// Save stats to localStorage
localStorage.setItem('flagQuizStats', JSON.stringify(gameStats));
// Check for new achievements
if (achievementsComponent) {
achievementsComponent.checkAchievements();
}
// Auto-advance to next question with different delays if auto mode is on
if (autoAdvance) {
const delay = isCorrect ? 2000 : 4000; // Double delay for wrong answers
@@ -219,9 +252,7 @@
generateQuestion();
}, delay);
}
}
function skipQuestion() {
} function skipQuestion() {
if (gameState !== 'question') return;
// Update skip counters
@@ -229,6 +260,16 @@
gameStats.skipped++;
gameStats.total++;
// Track consecutive skips for Speed Runner achievement
if (achievementsComponent) {
achievementsComponent.incrementConsecutiveSkips();
}
// Check for achievements
if (achievementsComponent) {
achievementsComponent.checkAchievements();
}
// Save stats to localStorage
localStorage.setItem('flagQuizStats', JSON.stringify(gameStats));
@@ -268,12 +309,32 @@
}
function resetAllStats() {
gameStats = { correct: 0, wrong: 0, skipped: 0 };
showResetConfirmation = true;
}
function confirmReset() {
// Reset game statistics
gameStats = { correct: 0, wrong: 0, total: 0, skipped: 0 };
score = { correct: 0, total: 0, skipped: 0 };
currentStreak = 0;
localStorage.setItem('flagQuizStats', JSON.stringify(gameStats));
// Reset achievements
if (achievementsComponent) {
localStorage.removeItem('flagQuizAchievements');
// Reinitialize achievements component
achievementsComponent.loadAchievements();
updateAchievementCount();
}
showResetConfirmation = false;
showSettings = false;
}
function cancelReset() {
showResetConfirmation = false;
}
function nextQuestion() {
generateQuestion();
}
@@ -285,9 +346,26 @@
function getFlagImage(flag) {
return `/images/flags/${flag.path}`;
}
function updateAchievementCount() {
if (achievementsComponent) {
achievementCount = achievementsComponent.getAchievementCount();
}
}
function handleAchievementsUnlocked() {
updateAchievementCount();
}
</script>
<Header {theme} {setTheme} {score} {gameStats} />
<Header
{theme}
{setTheme}
{score}
{gameStats}
{achievementCount}
onAchievementClick={() => showAchievements = true}
/>
<main class="flag-quiz">
<div class="container">
@@ -334,6 +412,41 @@
</div>
</div>
{/if}
<!-- Reset Confirmation Dialog -->
{#if showResetConfirmation}
<div class="confirmation-overlay" on:click={(e) => e.target === e.currentTarget && cancelReset()}>
<div class="confirmation-dialog">
<div class="confirmation-header">
<h3>⚠️ Reset All Data</h3>
</div>
<div class="confirmation-content">
<p>This action will permanently delete:</p>
<ul>
<li>✗ All game statistics (correct, wrong, skipped answers)</li>
<li>✗ Current session score</li>
<li>✗ All unlocked achievements</li>
<li>✗ Achievement progress</li>
</ul>
<p><strong>This cannot be undone!</strong></p>
</div>
<div class="confirmation-actions">
<button class="cancel-btn" on:click={cancelReset}>Cancel</button>
<button class="confirm-btn" on:click={confirmReset}>Reset Everything</button>
</div>
</div>
</div>
{/if}
<!-- Achievements Component -->
<Achievements
bind:this={achievementsComponent}
{gameStats}
{currentStreak}
show={showAchievements}
on:close={() => showAchievements = false}
on:achievementsUnlocked={handleAchievementsUnlocked}
/>
{#if gameState === 'loading'}
<div class="loading">Loading flags...</div>
{:else if currentQuestion}
@@ -350,12 +463,28 @@
{#if showResult}
<div class="result">
{#if selectedAnswer === correctAnswer}
<div class="correct-result"> Correct!</div>
<div class="correct-result"><span class="result-icon smile-icon"><InlineSvg path="/icons/smile-squre.svg" alt="Correct" /></span> Correct!</div>
{:else}
<div class="wrong-result">
Wrong!
<span class="result-icon sad-icon"><InlineSvg path="/icons/sad-square.svg" alt="Wrong" /></span> Wrong!
{#if currentQuestion.type === 'flag-to-country'}
The correct answer is: {getCountryName(currentQuestion.correct)}.
<span class="result-country-info">
The correct answer is: {getCountryName(currentQuestion.correct)}.
<button
class="info-icon result-info-btn"
aria-label="Show country info"
aria-expanded={showResultCountryInfo}
on:click={() => (showResultCountryInfo = !showResultCountryInfo)}
on:keydown={(e) => { if (e.key === 'Escape') showResultCountryInfo = false; }}
>
<InlineSvg path="/icons/info-square.svg" alt="Country info" />
</button>
{#if showResultCountryInfo}
<div class="info-tooltip result-info-tooltip" role="dialog" aria-live="polite">
{currentQuestion.correct.meta.description}
</div>
{/if}
</span>
{:else}
You selected the {getCountryName(currentQuestion.options[selectedAnswer])} flag.
{/if}
@@ -386,7 +515,25 @@
</div>
{:else}
<div class="country-display">
<h2 class="country-name">{getCountryName(currentQuestion.correct)}</h2>
<h2 class="country-name">
{getCountryName(currentQuestion.correct)}
{#if currentQuestion.correct?.meta?.description}
<button
class="info-icon"
aria-label="Show country info"
aria-expanded={showCountryInfo}
on:click={() => (showCountryInfo = !showCountryInfo)}
on:keydown={(e) => { if (e.key === 'Escape') showCountryInfo = false; }}
>
<InlineSvg path="/icons/info-square.svg" alt="Country info" />
</button>
{#if showCountryInfo}
<div class="info-tooltip" role="dialog" aria-live="polite">
{currentQuestion.correct.meta.description}
</div>
{/if}
{/if}
</h2>
</div>
<div class="flag-options">
@@ -414,9 +561,9 @@
{/if}
<div class="controls">
<a href="#/game" class="btn btn-secondary">Back to Games</a>
<button class="btn btn-secondary" on:click={resetGame}>New Session</button>
<button class="btn btn-secondary" on:click={toggleSettings} title="Settings">Settings</button>
<a href="#/game" class="btn btn-primary">Back to Games</a>
</div>
</main>
@@ -532,6 +679,101 @@
background: #cc3333;
}
/* Confirmation Dialog Styles */
.confirmation-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.6);
display: flex;
justify-content: center;
align-items: center;
z-index: 1001;
}
.confirmation-dialog {
background: var(--color-bg-primary);
border: 2px solid var(--color-border);
border-radius: 1rem;
padding: 0;
max-width: 500px;
width: 90%;
box-shadow: 0 10px 24px rgba(0,0,0,0.25);
}
.confirmation-header {
padding: 1.5rem 1.5rem 1rem 1.5rem;
border-bottom: 1px solid var(--color-border);
}
.confirmation-header h3 {
margin: 0;
font-size: 1.3rem;
color: #dc2626;
}
.confirmation-content {
padding: 1.5rem;
}
.confirmation-content p {
margin: 0 0 1rem 0;
color: var(--color-text-primary);
}
.confirmation-content ul {
margin: 1rem 0;
padding-left: 1.5rem;
color: var(--color-text-secondary);
}
.confirmation-content li {
margin: 0.5rem 0;
}
.confirmation-actions {
display: flex;
gap: 1rem;
padding: 1rem 1.5rem 1.5rem 1.5rem;
justify-content: flex-end;
border-top: 1px solid var(--color-border);
}
.cancel-btn {
background: var(--color-bg-secondary);
border: 1px solid var(--color-border);
color: var(--color-text-primary);
padding: 0.75rem 1.5rem;
border-radius: 0.5rem;
font-size: 0.9rem;
cursor: pointer;
transition: all 0.2s ease;
}
.cancel-btn:hover {
background: var(--color-bg-hover);
border-color: var(--color-primary);
}
.confirm-btn {
background: #dc2626;
border: 1px solid #dc2626;
color: white;
padding: 0.75rem 1.5rem;
border-radius: 0.5rem;
font-size: 0.9rem;
font-weight: 600;
cursor: pointer;
transition: all 0.2s ease;
}
.confirm-btn:hover {
background: #b91c1c;
border-color: #b91c1c;
}
.loading {
text-align: center;
@@ -592,6 +834,7 @@
.country-display {
text-align: center;
margin-bottom: 2rem;
position: relative;
}
.country-name {
@@ -601,6 +844,52 @@
margin: 0;
}
.info-icon {
margin-left: 0.5rem;
width: 2rem;
height: 2rem;
vertical-align: middle;
background: none;
color: var(--color-text-primary);
cursor: pointer;
padding: 0;
display: inline-flex;
align-items: center;
justify-content: center;
margin-bottom: 5px;
}
.info-icon :global(.svg-wrapper) {
width: 100%;
height: 100%;
}
.info-icon:hover,
.info-icon:focus {
color: var(--color-text-primary);
border-color: var(--color-border);
outline: none;
}
.info-tooltip {
position: absolute;
left: 50%;
top: calc(100% + 8px);
transform: translateX(-50%);
background: var(--color-bg-secondary);
color: var(--color-text-primary);
border: 1px solid var(--color-border);
border-radius: 8px;
padding: 0.75rem 1rem;
width: min(90vw, 520px);
max-height: 40vh;
overflow: auto;
box-shadow: 0 8px 20px rgba(0,0,0,0.2);
z-index: 5;
text-align: left;
font-size: 0.95rem;
}
.options {
display: grid;
gap: 1rem;
@@ -700,6 +989,100 @@
color: #ef4444;
}
.result-icon {
display: inline-flex;
width: 24px;
height: 24px;
vertical-align: middle;
margin-right: 0.5rem;
}
.result-icon.smile-icon {
color: #22c55e; /* green for correct */
animation: correctBounce 0.6s ease-out;
}
.result-icon.sad-icon {
color: #ef4444; /* red for wrong */
animation: wrongShake 0.5s ease-in-out;
}
@keyframes correctBounce {
0% {
transform: scale(0) rotate(0deg);
opacity: 0;
}
50% {
transform: scale(1.3) rotate(5deg);
opacity: 1;
}
100% {
transform: scale(1) rotate(0deg);
opacity: 1;
}
}
@keyframes wrongShake {
0% {
transform: translateX(0) scale(0);
opacity: 0;
}
25% {
transform: translateX(-5px) scale(1);
opacity: 1;
}
50% {
transform: translateX(5px) scale(1);
}
75% {
transform: translateX(-3px) scale(1);
}
100% {
transform: translateX(0) scale(1);
opacity: 1;
}
}
.result-country-info {
position: relative;
display: inline-flex;
align-items: center;
gap: 0.5rem;
}
.result-info-btn {
margin-left: 0.25rem;
width: 2rem;
height: 2rem;
vertical-align: middle;
background: none;
border: none;
color: var(--color-text-primary);
cursor: pointer;
padding: 0;
display: inline-flex;
align-items: center;
justify-content: center;
opacity: 0.7;
transition: opacity 0.2s;
}
.result-info-btn:hover,
.result-info-btn:focus {
opacity: 1;
outline: none;
}
.result-info-tooltip {
position: absolute;
top: 100%;
left: 50%;
transform: translateX(-50%);
margin-top: 0.5rem;
min-width: 200px;
max-width: 300px;
}
.controls {
display: flex;
justify-content: center;
@@ -757,8 +1140,8 @@
@media (max-width: 768px) {
.container {
padding: 0.75rem;
padding-top: 0.75rem;
padding: 0.75rem;
padding-top: 0.75rem;
}
.options {
@@ -775,6 +1158,12 @@
max-height: 180px;
}
.info-tooltip {
width: 92vw;
left: 50%;
transform: translateX(-50%);
}
.controls {
flex-direction: row;
flex-wrap: nowrap;