Projects STRLCPY gradejs Commits 3ee21f7b
🤬
  • ■ ■ ■ ■ ■
    packages/web/.storybook/main.js
    skipped 15 lines
    16 16   {
    17 17   loader: 'css-loader',
    18 18   options: {
    19  - modules: true,
     19 + modules: {
     20 + exportLocalsConvention: 'camelCase',
     21 + },
    20 22   sourceMap: true,
    21 23   },
    22 24   },
    skipped 34 lines
  • packages/web/src/assets/fonts/Onest/OnestBlack.woff
    Binary file.
  • packages/web/src/assets/fonts/Onest/OnestBlack.woff2
    Binary file.
  • packages/web/src/assets/fonts/Onest/OnestBold.woff
    Binary file.
  • packages/web/src/assets/fonts/Onest/OnestBold.woff2
    Binary file.
  • packages/web/src/assets/fonts/Onest/OnestExtraBold.woff
    Binary file.
  • packages/web/src/assets/fonts/Onest/OnestExtraBold.woff2
    Binary file.
  • packages/web/src/assets/fonts/Onest/OnestLight.woff
    Binary file.
  • packages/web/src/assets/fonts/Onest/OnestLight.woff2
    Binary file.
  • packages/web/src/assets/fonts/Onest/OnestMedium.woff
    Binary file.
  • packages/web/src/assets/fonts/Onest/OnestMedium.woff2
    Binary file.
  • packages/web/src/assets/fonts/Onest/OnestRegular.woff
    Binary file.
  • packages/web/src/assets/fonts/Onest/OnestRegular.woff2
    Binary file.
  • packages/web/src/assets/fonts/Onest/OnestThin.woff
    Binary file.
  • packages/web/src/assets/fonts/Onest/OnestThin.woff2
    Binary file.
  • packages/web/src/assets/fonts/RobotoMono/RobotoMono-Medium.woff
    Binary file.
  • packages/web/src/assets/fonts/RobotoMono/RobotoMono-Medium.woff2
    Binary file.
  • packages/web/src/assets/fonts/RobotoMono/RobotoMono-Regular.woff
    Binary file.
  • packages/web/src/assets/fonts/RobotoMono/RobotoMono-Regular.woff2
    Binary file.
  • packages/web/src/assets/hero-bg.png
  • ■ ■ ■ ■ ■ ■
    packages/web/src/components/icons/Arrow.tsx
     1 +import React from 'react';
     2 +import { IconProps } from './types';
     3 + 
     4 +export default function Arrow({
     5 + width = '24',
     6 + height = '24',
     7 + color = '#8E8AA0',
     8 + className,
     9 +}: IconProps) {
     10 + return (
     11 + <svg
     12 + width={width}
     13 + height={height}
     14 + className={className}
     15 + viewBox='0 0 10 18'
     16 + fill='none'
     17 + xmlns='http://www.w3.org/2000/svg'
     18 + >
     19 + <path
     20 + d='M1 1.67139L9 9.17139L1 16.6714'
     21 + stroke={color}
     22 + strokeWidth='2'
     23 + strokeLinecap='round'
     24 + strokeLinejoin='round'
     25 + />
     26 + </svg>
     27 + );
     28 +}
     29 + 
  • ■ ■ ■ ■ ■ ■
    packages/web/src/components/icons/Bug.tsx
     1 +import React from 'react';
     2 +import { IconProps } from './types';
     3 + 
     4 +export default function Bug({
     5 + width = '24',
     6 + height = '24',
     7 + color = '#F3512E',
     8 + className,
     9 +}: IconProps) {
     10 + return (
     11 + <svg
     12 + width={width}
     13 + height={height}
     14 + className={className}
     15 + viewBox='0 0 24 23'
     16 + fill='none'
     17 + xmlns='http://www.w3.org/2000/svg'
     18 + >
     19 + <g opacity='0.5'>
     20 + <path
     21 + d='M19.1126 11.9117C19.1126 12.5345 19.0636 13.1427 18.9703 13.7303C18.7474 15.1342 18.2715 16.4206 17.6061 17.5085C16.3042 19.6367 14.277 21.0048 11.9998 21.0048C9.7226 21.0048 7.69536 19.6367 6.3935 17.5085C5.72808 16.4206 5.25217 15.1342 5.02925 13.7303C4.93595 13.1427 4.88697 12.5345 4.88697 11.9117C4.88697 10.8579 5.0272 9.84592 5.2851 8.90466C5.51141 8.07871 5.82833 7.3072 6.22062 6.60961C6.9705 5.27616 7.99577 4.21282 9.19 3.55564C10.0519 3.08134 11.0019 2.81862 11.9998 2.81862C12.9977 2.81862 13.9476 3.08134 14.8096 3.55564C16.0038 4.21282 17.0291 5.27616 17.7789 6.60961C18.1712 7.3072 18.4882 8.07871 18.7145 8.90466C18.9724 9.84592 19.1126 10.8579 19.1126 11.9117Z'
     22 + fill={color}
     23 + />
     24 + <path
     25 + d='M11.9998 21.0048V8.27447M11.9998 21.0048C14.277 21.0048 16.3042 19.6367 17.6061 17.5085M11.9998 21.0048C9.7226 21.0048 7.69536 19.6367 6.3935 17.5085M11.9998 8.27447C11.1107 8.27447 8.35447 8.18751 6.22062 6.60961M11.9998 8.27447C12.8889 8.27447 15.6451 8.18354 17.7789 6.60961M6.22062 6.60961C5.82833 7.3072 5.51141 8.07871 5.2851 8.90466M6.22062 6.60961C6.9705 5.27616 7.99577 4.21282 9.19 3.55564M17.7789 6.60961C18.1712 7.3072 18.4882 8.07871 18.7145 8.90466M17.7789 6.60961C17.0291 5.27616 16.0038 4.21282 14.8096 3.55564M17.6061 17.5085L20.8908 20.0955M17.6061 17.5085C18.2715 16.4206 18.7474 15.1342 18.9703 13.7303M18.9703 13.7303C19.0636 13.1427 19.1126 12.5345 19.1126 11.9117C19.1126 10.8579 18.9724 9.84592 18.7145 8.90466M18.9703 13.7303H22.669M18.7145 8.90466L21.7799 7.36516M5.2851 8.90466C5.0272 9.84592 4.88697 10.8579 4.88697 11.9117C4.88697 12.5345 4.93595 13.1427 5.02925 13.7303M5.2851 8.90466L2.21967 7.36516M6.66517 1L9.19 3.55564M9.19 3.55564C10.0519 3.08134 11.0019 2.81862 11.9998 2.81862C12.9977 2.81862 13.9476 3.08134 14.8096 3.55564M17.3344 1L14.8096 3.55564M1.33057 13.7303C2.56346 13.7303 5.02925 13.7303 5.02925 13.7303M5.02925 13.7303C5.25217 15.1342 5.72808 16.4206 6.3935 17.5085M3.10877 20.0955L6.3935 17.5085'
     26 + stroke={color}
     27 + strokeWidth='2'
     28 + strokeLinecap='round'
     29 + />
     30 + </g>
     31 + <path
     32 + d='M6.22119 6.60961C8.35503 8.18751 11.1112 8.27447 12.0004 8.27447C12.8895 8.27447 15.6457 8.18354 17.7795 6.60961C17.0296 5.27616 16.0044 4.21282 14.8101 3.55564C13.9482 3.08134 12.9983 2.81862 12.0004 2.81862C11.0024 2.81862 10.0525 3.08134 9.19057 3.55564C7.99633 4.21282 6.97106 5.27616 6.22119 6.60961Z'
     33 + fill={color}
     34 + />
     35 + <path
     36 + d='M12.0004 21.0048V8.27447M12.0004 8.27447C11.1112 8.27447 8.35503 8.18751 6.22119 6.60961C6.97106 5.27616 7.99633 4.21282 9.19057 3.55564M12.0004 8.27447C12.8895 8.27447 15.6457 8.18354 17.7795 6.60961C17.0296 5.27616 16.0044 4.21282 14.8101 3.55564M6.66574 1L9.19057 3.55564M9.19057 3.55564C10.0525 3.08134 11.0024 2.81862 12.0004 2.81862C12.9983 2.81862 13.9482 3.08134 14.8101 3.55564M17.335 1L14.8101 3.55564'
     37 + stroke={color}
     38 + strokeWidth='2'
     39 + strokeLinecap='round'
     40 + />
     41 + </svg>
     42 + );
     43 +}
     44 + 
  • ■ ■ ■ ■ ■ ■
    packages/web/src/components/icons/Logo.tsx
     1 +import React from 'react';
     2 +import { IconProps } from './types';
     3 + 
     4 +export default function Logo({
     5 + width = '129',
     6 + height = '25',
     7 + color = '#fff',
     8 + className,
     9 +}: IconProps) {
     10 + return (
     11 + <svg
     12 + width={width}
     13 + height={height}
     14 + className={className}
     15 + viewBox='0 0 129 25'
     16 + fill='none'
     17 + xmlns='http://www.w3.org/2000/svg'
     18 + >
     19 + <path
     20 + d='M11.6023 24.3182C9.42803 24.3182 7.51515 23.8371 5.86364 22.875C4.21212 21.9053 2.92424 20.5303 2 18.75C1.07576 16.9621 0.613636 14.8409 0.613636 12.3864C0.613636 10.5 0.893939 8.81818 1.45455 7.34091C2.01515 5.85606 2.79545 4.59848 3.79545 3.56818C4.80303 2.53788 5.97348 1.75379 7.30682 1.21591C8.64015 0.678029 10.072 0.40909 11.6023 0.40909C12.9205 0.40909 14.1515 0.602272 15.2955 0.988636C16.447 1.36742 17.4735 1.9053 18.375 2.60227C19.2765 3.29167 20.0114 4.11742 20.5795 5.07954C21.1553 6.03409 21.5227 7.09091 21.6818 8.25H16.7045C16.5455 7.69697 16.3144 7.20833 16.0114 6.78409C15.7159 6.35227 15.3485 5.98864 14.9091 5.69318C14.4773 5.39015 13.9886 5.15909 13.4432 5C12.9053 4.84091 12.3182 4.76136 11.6818 4.76136C10.5 4.76136 9.45076 5.05682 8.53409 5.64773C7.61742 6.23864 6.89773 7.09848 6.375 8.22727C5.85985 9.34848 5.60227 10.7197 5.60227 12.3409C5.60227 13.9621 5.84849 15.3409 6.34091 16.4773C6.84091 17.6136 7.54167 18.4811 8.44318 19.0795C9.3447 19.6705 10.4053 19.9659 11.625 19.9659C12.7614 19.9659 13.7311 19.7614 14.5341 19.3523C15.3371 18.9356 15.9508 18.3598 16.375 17.625C16.8068 16.8826 17.0227 16.0265 17.0227 15.0568L17.9545 15.25H11.9318V11.5455H21.875V24H18.5114L18.2273 21.3409H18.1023C17.322 22.2879 16.3977 23.0227 15.3295 23.5455C14.2689 24.0606 13.0265 24.3182 11.6023 24.3182ZM24.5031 24V6.54545H29.1963V9.59091H29.3781C29.6963 8.50758 30.2303 7.68939 30.9803 7.13636C31.7303 6.57576 32.594 6.29545 33.5713 6.29545C34.0409 6.29545 34.4766 6.35227 34.8781 6.46591C35.2796 6.57955 35.6394 6.73485 35.9576 6.93182L34.5031 10.9318C34.2758 10.8182 34.0258 10.7235 33.7531 10.6477C33.4879 10.5644 33.1887 10.5227 32.8553 10.5227C32.2038 10.5227 31.6129 10.678 31.0826 10.9886C30.5523 11.2917 30.1319 11.7121 29.8213 12.25C29.5106 12.7879 29.3516 13.4129 29.344 14.125V24H24.5031ZM42.6624 24.2841C41.3366 24.2841 40.1359 23.9432 39.0601 23.2614C37.9919 22.572 37.1434 21.5606 36.5147 20.2273C35.8934 18.8864 35.5828 17.2424 35.5828 15.2955C35.5828 13.2955 35.9048 11.6326 36.5488 10.3068C37.1927 8.97348 38.0488 7.97727 39.1169 7.31818C40.1927 6.65152 41.3707 6.31818 42.651 6.31818C43.6283 6.31818 44.4427 6.48485 45.0942 6.81818C45.7533 7.14394 46.2836 7.55303 46.6851 8.04545C47.0942 8.5303 47.4048 9.00758 47.6169 9.47727H47.7647V6.54545H52.5942V24H47.8215V21.2045H47.6169C47.3897 21.6894 47.0677 22.1705 46.651 22.6477C46.2419 23.1174 45.7078 23.5076 45.0488 23.8182C44.3972 24.1288 43.6018 24.2841 42.6624 24.2841ZM44.1965 20.4318C44.9768 20.4318 45.6359 20.2197 46.1738 19.7955C46.7192 19.3636 47.1359 18.7614 47.4238 17.9886C47.7192 17.2159 47.8669 16.3106 47.8669 15.2727C47.8669 14.2348 47.723 13.3333 47.4351 12.5682C47.1472 11.803 46.7306 11.2121 46.1851 10.7955C45.6397 10.3788 44.9768 10.1705 44.1965 10.1705C43.401 10.1705 42.7306 10.3864 42.1851 10.8182C41.6397 11.25 41.2268 11.8485 40.9465 12.6136C40.6662 13.3788 40.526 14.2652 40.526 15.2727C40.526 16.2879 40.6662 17.1856 40.9465 17.9659C41.2344 18.7386 41.6472 19.3447 42.1851 19.7841C42.7306 20.2159 43.401 20.4318 44.1965 20.4318ZM62.0461 24.2841C60.7204 24.2841 59.5196 23.9432 58.4439 23.2614C57.3757 22.572 56.5272 21.5606 55.8984 20.2273C55.2772 18.8864 54.9666 17.2424 54.9666 15.2955C54.9666 13.2955 55.2886 11.6326 55.9325 10.3068C56.5764 8.97348 57.4325 7.97727 58.5007 7.31818C59.5764 6.65152 60.7545 6.31818 62.0348 6.31818C63.012 6.31818 63.8264 6.48485 64.478 6.81818C65.137 7.14394 65.6673 7.55303 66.0689 8.04545C66.478 8.5303 66.7886 9.00758 67.0007 9.47727H67.1484V0.727272H71.978V24H67.2052V21.2045H67.0007C66.7734 21.6894 66.4514 22.1705 66.0348 22.6477C65.6257 23.1174 65.0916 23.5076 64.4325 23.8182C63.781 24.1288 62.9855 24.2841 62.0461 24.2841ZM63.5802 20.4318C64.3605 20.4318 65.0196 20.2197 65.5575 19.7955C66.103 19.3636 66.5196 18.7614 66.8075 17.9886C67.103 17.2159 67.2507 16.3106 67.2507 15.2727C67.2507 14.2348 67.1067 13.3333 66.8189 12.5682C66.531 11.803 66.1143 11.2121 65.5689 10.7955C65.0234 10.3788 64.3605 10.1705 63.5802 10.1705C62.7848 10.1705 62.1143 10.3864 61.5689 10.8182C61.0234 11.25 60.6105 11.8485 60.3302 12.6136C60.0499 13.3788 59.9098 14.2652 59.9098 15.2727C59.9098 16.2879 60.0499 17.1856 60.3302 17.9659C60.6181 18.7386 61.031 19.3447 61.5689 19.7841C62.1143 20.2159 62.7848 20.4318 63.5802 20.4318ZM82.9526 24.3409C81.1572 24.3409 79.6117 23.9773 78.3163 23.25C77.0284 22.5152 76.036 21.4773 75.339 20.1364C74.642 18.7879 74.2935 17.1932 74.2935 15.3523C74.2935 13.5568 74.642 11.9811 75.339 10.625C76.036 9.26894 77.017 8.21212 78.2822 7.45454C79.5549 6.69697 81.0473 6.31818 82.7594 6.31818C83.911 6.31818 84.9829 6.50379 85.9753 6.875C86.9753 7.23864 87.8466 7.78788 88.589 8.52273C89.339 9.25758 89.9223 10.1818 90.339 11.2955C90.7556 12.4015 90.964 13.697 90.964 15.1818V16.5114H76.2253V13.5114H86.4072C86.4072 12.8144 86.2556 12.197 85.9526 11.6591C85.6496 11.1212 85.2291 10.7008 84.6913 10.3977C84.161 10.0871 83.5435 9.93182 82.839 9.93182C82.1041 9.93182 81.4526 10.1023 80.8844 10.4432C80.3238 10.7765 79.8844 11.2273 79.5663 11.7955C79.2481 12.3561 79.0852 12.9811 79.0776 13.6705V16.5227C79.0776 17.3864 79.2367 18.1326 79.5549 18.7614C79.8806 19.3902 80.339 19.875 80.9299 20.2159C81.5208 20.5568 82.2216 20.7273 83.0322 20.7273C83.57 20.7273 84.0625 20.6515 84.5094 20.5C84.9564 20.3485 85.339 20.1212 85.6572 19.8182C85.9753 19.5152 86.2178 19.1439 86.3844 18.7045L90.8617 19C90.6344 20.0758 90.1685 21.0152 89.464 21.8182C88.767 22.6136 87.8655 23.2348 86.7594 23.6818C85.661 24.1212 84.392 24.3409 82.9526 24.3409ZM102.572 0.727272H107.436V16.9545C107.436 18.4545 107.099 19.7576 106.424 20.8636C105.758 21.9697 104.83 22.822 103.64 23.4205C102.451 24.0189 101.068 24.3182 99.4926 24.3182C98.0911 24.3182 96.8184 24.072 95.6744 23.5795C94.5381 23.0795 93.6366 22.322 92.9699 21.3068C92.3032 20.2841 91.9737 19 91.9812 17.4545H96.879C96.8941 18.0682 97.0191 18.5947 97.254 19.0341C97.4964 19.4659 97.8259 19.7992 98.2426 20.0341C98.6669 20.2614 99.1669 20.375 99.7426 20.375C100.349 20.375 100.86 20.2462 101.277 19.9886C101.701 19.7235 102.023 19.3371 102.243 18.8295C102.462 18.322 102.572 17.697 102.572 16.9545V0.727272ZM123.169 7.42045C123.078 6.50379 122.688 5.79167 121.999 5.28409C121.309 4.77651 120.374 4.52273 119.192 4.52273C118.389 4.52273 117.711 4.63636 117.158 4.86364C116.605 5.08333 116.18 5.39015 115.885 5.78409C115.597 6.17803 115.453 6.625 115.453 7.125C115.438 7.54167 115.525 7.9053 115.714 8.21591C115.911 8.52652 116.18 8.79545 116.521 9.02273C116.862 9.24242 117.256 9.43561 117.703 9.60227C118.15 9.76136 118.627 9.89773 119.135 10.0114L121.226 10.5114C122.241 10.7386 123.173 11.0417 124.021 11.4205C124.87 11.7992 125.605 12.2652 126.226 12.8182C126.847 13.3712 127.328 14.0227 127.669 14.7727C128.017 15.5227 128.195 16.3826 128.203 17.3523C128.195 18.7765 127.832 20.0114 127.112 21.0568C126.4 22.0947 125.37 22.9015 124.021 23.4773C122.68 24.0455 121.063 24.3295 119.169 24.3295C117.29 24.3295 115.654 24.0417 114.26 23.4659C112.874 22.8902 111.79 22.0379 111.01 20.9091C110.237 19.7727 109.832 18.3674 109.794 16.6932H114.555C114.608 17.4735 114.832 18.125 115.226 18.6477C115.627 19.1629 116.161 19.553 116.828 19.8182C117.502 20.0758 118.264 20.2045 119.112 20.2045C119.945 20.2045 120.669 20.0833 121.283 19.8409C121.904 19.5985 122.385 19.2614 122.726 18.8295C123.067 18.3977 123.237 17.9015 123.237 17.3409C123.237 16.8182 123.082 16.3788 122.771 16.0227C122.468 15.6667 122.021 15.3636 121.43 15.1136C120.847 14.8636 120.131 14.6364 119.283 14.4318L116.749 13.7955C114.786 13.3182 113.237 12.572 112.101 11.5568C110.964 10.5417 110.4 9.17424 110.408 7.45454C110.4 6.04545 110.775 4.81439 111.533 3.76136C112.298 2.70833 113.347 1.88636 114.68 1.29545C116.014 0.704545 117.529 0.40909 119.226 0.40909C120.953 0.40909 122.461 0.704545 123.749 1.29545C125.044 1.88636 126.052 2.70833 126.771 3.76136C127.491 4.81439 127.862 6.03409 127.885 7.42045H123.169Z'
     21 + fill={color}
     22 + />
     23 + </svg>
     24 + );
     25 +}
     26 + 
  • ■ ■ ■ ■ ■ ■
    packages/web/src/components/icons/Spinner.tsx
     1 +import React from 'react';
     2 +import { IconProps } from './types';
     3 + 
     4 +type SpinnerProps = IconProps & {
     5 + fromColor?: string;
     6 +};
     7 + 
     8 +export default function Spinner({
     9 + width = '32',
     10 + height = '32',
     11 + color = '#fff',
     12 + fromColor = '#009933',
     13 + className,
     14 +}: SpinnerProps) {
     15 + return (
     16 + <svg
     17 + width={width}
     18 + height={height}
     19 + className={className}
     20 + xmlns='http://www.w3.org/2000/svg'
     21 + fill='none'
     22 + viewBox='0 0 32 32'
     23 + >
     24 + <path
     25 + stroke='url(#spinner)'
     26 + strokeLinecap='round'
     27 + strokeLinejoin='round'
     28 + strokeWidth='4'
     29 + d='M16 30c7.732 0 14-6.268 14-14 0-7.73199-6.268-14-14-14C8.26801 2 2 8.26801 2 16c0 7.732 6.26801 14 14 14Z'
     30 + />
     31 + <defs>
     32 + <radialGradient
     33 + id='spinner'
     34 + cx='0'
     35 + cy='0'
     36 + r='1'
     37 + gradientTransform='matrix(0 -14 14 0 16 16)'
     38 + gradientUnits='userSpaceOnUse'
     39 + >
     40 + <stop stopColor={color} />
     41 + <stop offset='.0001' stopColor={fromColor} stopOpacity='0' />
     42 + <stop offset='.140625' stopColor={color} stopOpacity='0' />
     43 + </radialGradient>
     44 + </defs>
     45 + </svg>
     46 + );
     47 +}
     48 + 
  • ■ ■ ■ ■ ■ ■
    packages/web/src/components/ui/Card/Card.module.scss
     1 +@import '~styles/_vars.scss';
     2 +@import '~styles/responsive.scss';
     3 + 
     4 +.card {
     5 + position: relative;
     6 + background: #ffffff;
     7 + border-radius: 24px;
     8 + padding: 24px;
     9 + box-shadow: $shadow;
     10 + width: 100%;
     11 + max-width: 600px;
     12 + min-height: 248px;
     13 + display: flex;
     14 + flex-direction: column;
     15 + 
     16 + @include mobile-and-tablet {
     17 + min-height: 204px;
     18 + }
     19 +}
     20 + 
     21 +.header {
     22 + display: flex;
     23 + align-items: center;
     24 +}
     25 + 
     26 +.icon {
     27 + flex-shrink: 0;
     28 + margin-right: 16px;
     29 + display: inline-flex;
     30 + 
     31 + img {
     32 + width: 100%;
     33 + max-width: 52px;
     34 + object-fit: cover;
     35 + }
     36 +}
     37 + 
     38 +.title {
     39 + font-size: 26px;
     40 + font-weight: 500;
     41 + line-height: 1.54;
     42 + 
     43 + @include mobile-and-tablet {
     44 + font-size: 20px;
     45 + line-height: 1.4;
     46 + }
     47 +}
     48 + 
     49 +.description {
     50 + margin-top: 4px;
     51 + text-overflow: ellipsis;
     52 + overflow: hidden;
     53 + white-space: nowrap;
     54 + color: $gray-text;
     55 +}
     56 + 
     57 +.tagsWrapper {
     58 + margin-top: 28px;
     59 + flex: 1;
     60 + display: flex;
     61 + flex-direction: column;
     62 + 
     63 + @include mobile-and-tablet {
     64 + margin-top: 20px;
     65 + }
     66 +}
     67 + 
     68 +.tags {
     69 + display: flex;
     70 + flex-wrap: wrap;
     71 + align-items: center;
     72 + justify-content: flex-start;
     73 + 
     74 + & > * {
     75 + margin-bottom: 0.5rem;
     76 + }
     77 + 
     78 + & > *:not(:last-child) {
     79 + margin-right: 0.75rem;
     80 + }
     81 +}
     82 + 
     83 +.sitesWrapper {
     84 + flex: 1;
     85 + display: flex;
     86 + flex-direction: column;
     87 + justify-content: flex-end;
     88 +}
     89 + 
     90 +.sites {
     91 + display: flex;
     92 + align-items: center;
     93 +}
     94 + 
     95 +.sites-list {
     96 + padding-left: 0;
     97 + margin-top: 0;
     98 + margin-bottom: 0;
     99 + list-style: none;
     100 + flex-shrink: 0;
     101 + display: flex;
     102 + align-items: center;
     103 + flex-direction: row-reverse;
     104 + 
     105 + & > * + * {
     106 + margin-left: -7px;
     107 + }
     108 +}
     109 + 
     110 +.sites-item {
     111 + width: 40px;
     112 + height: 40px;
     113 + border: 2px solid $white;
     114 + border-radius: 50%;
     115 + overflow: hidden;
     116 + flex-shrink: 0;
     117 + display: flex;
     118 + align-items: center;
     119 + justify-content: center;
     120 + user-select: none;
     121 + margin-left: -7px;
     122 + 
     123 + &:last-child {
     124 + margin-left: 0;
     125 + }
     126 + 
     127 + img {
     128 + width: 100%;
     129 + height: 100%;
     130 + object-fit: cover;
     131 + }
     132 + 
     133 + @include mobile-and-tablet {
     134 + width: 36px;
     135 + height: 36px;
     136 + }
     137 +}
     138 + 
     139 +.sites-count {
     140 + white-space: nowrap;
     141 + font-size: 19px;
     142 + line-height: 30px;
     143 + margin-left: 16px;
     144 + 
     145 + @include mobile-and-tablet {
     146 + font-size: 16px;
     147 + line-height: 1.62;
     148 + }
     149 +}
     150 + 
     151 +.toAll {
     152 + background-color: $gray-surface;
     153 +}
     154 + 
     155 +.arrowBtn {
     156 + cursor: pointer;
     157 + position: absolute;
     158 + width: 48px;
     159 + height: 48px;
     160 + bottom: 24px;
     161 + right: 24px;
     162 + background: $white;
     163 + border-radius: 50%;
     164 + display: flex;
     165 + align-items: center;
     166 + justify-content: center;
     167 + box-shadow: $shadow;
     168 + border: none;
     169 + appearance: none;
     170 + padding: 0;
     171 +}
     172 + 
     173 +.vulnerable {
     174 + min-height: 136px;
     175 + justify-content: space-between;
     176 + max-width: initial;
     177 + 
     178 + .title {
     179 + font-size: 24px;
     180 + 
     181 + @include mobile-and-tablet {
     182 + font-size: 20px;
     183 + }
     184 + }
     185 + 
     186 + @include mobile-and-tablet {
     187 + min-height: 136px;
     188 + }
     189 +}
     190 + 
     191 +.vulnerableWrapper {
     192 + display: flex;
     193 + align-items: center;
     194 + margin-top: 16px;
     195 + margin-right: -9px;
     196 +}
     197 + 
     198 +.vulnerablePackage {
     199 + position: relative;
     200 + display: flex;
     201 + align-items: center;
     202 + font-size: 18px;
     203 + line-height: 24px;
     204 + font-family: $font-monospace;
     205 + color: $red-accent;
     206 + 
     207 + @include mobile-and-tablet {
     208 + font-size: 14px;
     209 + line-height: 20px;
     210 + }
     211 +}
     212 + 
     213 +.vulnerableIcon {
     214 + display: flex;
     215 + margin-right: 12px;
     216 +}
     217 + 
     218 +.vulnerableMore {
     219 + margin-left: 12px;
     220 +}
     221 + 
     222 +.vulnerableMoreText {
     223 + @include mobile-and-tablet {
     224 + display: none;
     225 + }
     226 +}
     227 + 
  • ■ ■ ■ ■ ■ ■
    packages/web/src/components/ui/Card/Card.stories.tsx
     1 +import React from 'react';
     2 +import { ComponentMeta, ComponentStory } from '@storybook/react';
     3 +import Card from './Card';
     4 + 
     5 +export default {
     6 + title: 'Interface / Card',
     7 + component: Card,
     8 + parameters: {
     9 + layout: 'centered',
     10 + },
     11 +} as ComponentMeta<typeof Card>;
     12 + 
     13 +const Template: ComponentStory<typeof Card> = (args) => <Card {...args} />;
     14 + 
     15 +export const PopularSearchQueries = Template.bind({});
     16 +PopularSearchQueries.args = {
     17 + title: 'github.com',
     18 + icon: 'https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg',
     19 + packageTags: {
     20 + featuredPackages: ['mdast-util-from-markdown', 'react', 'react-dom'],
     21 + restPackages: 45,
     22 + },
     23 +};
     24 + 
     25 +export const PopularPackages = Template.bind({});
     26 +PopularPackages.args = {
     27 + title: '@team-griffin/react-heading-section',
     28 + description:
     29 + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.',
     30 + featuredSites: {
     31 + iconList: [
     32 + 'https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg',
     33 + 'https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg',
     34 + 'https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg',
     35 + ],
     36 + numberOfUses: 5265,
     37 + },
     38 +};
     39 + 
     40 +export const VulnerableSites = Template.bind({});
     41 +VulnerableSites.args = {
     42 + title: 'disneyland.omsk.ru/signup',
     43 + vulnerablePackage: {
     44 + name: 'mdast-util-from-markdown',
     45 + moreCount: 1,
     46 + },
     47 + variant: 'vulnerable',
     48 +};
     49 + 
  • ■ ■ ■ ■ ■ ■
    packages/web/src/components/ui/Card/Card.tsx
     1 +import React from 'react';
     2 +import styles from './Card.module.scss';
     3 +import clsx from 'clsx';
     4 +import { numberWithSpaces } from '../../../utils/helpers';
     5 +import { Bug } from 'components/icons';
     6 +import Chip from '../Chip/Chip';
     7 + 
     8 +export type CardProps = {
     9 + title: string;
     10 + icon?: string;
     11 + description?: string;
     12 + packageTags?: {
     13 + featuredPackages: string[];
     14 + restPackages: number;
     15 + };
     16 + featuredSites?: {
     17 + iconList: string[];
     18 + numberOfUses: number;
     19 + };
     20 + vulnerablePackage?: {
     21 + name: string;
     22 + moreCount?: number;
     23 + };
     24 + variant?: 'to-all' | 'vulnerable' | string;
     25 +};
     26 + 
     27 +export default function Card({
     28 + title,
     29 + icon,
     30 + description,
     31 + packageTags,
     32 + featuredSites,
     33 + vulnerablePackage,
     34 + variant = '',
     35 +}: CardProps) {
     36 + return (
     37 + <div className={clsx(styles.card, styles[variant])}>
     38 + <div className={styles.cardTop}>
     39 + <header className={styles.header}>
     40 + {icon && (
     41 + <div className={styles.icon}>
     42 + <img src={icon} alt='' />
     43 + </div>
     44 + )}
     45 + 
     46 + <div className={styles.title}>{title}</div>
     47 + </header>
     48 + 
     49 + {description && <div className={styles.description}>{description}</div>}
     50 + </div>
     51 + 
     52 + {variant === 'toAll' && (
     53 + <button className={styles.arrowBtn} type='button'>
     54 + {/* TODO: properly import SVG arrow */}
     55 + <svg
     56 + width='24'
     57 + height='25'
     58 + viewBox='0 0 24 25'
     59 + fill='none'
     60 + xmlns='http://www.w3.org/2000/svg'
     61 + >
     62 + <path
     63 + d='M9 5.5L17 13L9 20.5'
     64 + stroke='#212121'
     65 + strokeWidth='2'
     66 + strokeLinecap='round'
     67 + strokeLinejoin='round'
     68 + />
     69 + </svg>
     70 + </button>
     71 + )}
     72 + 
     73 + {packageTags && (
     74 + <div className={styles.tagsWrapper}>
     75 + <div className={styles.tags}>
     76 + {packageTags.featuredPackages.map((tag) => (
     77 + <Chip key={tag} size='large' font='monospace'>
     78 + {tag}
     79 + </Chip>
     80 + ))}
     81 + <Chip variant='outlined' size='large'>
     82 + +{packageTags.restPackages} packages
     83 + </Chip>
     84 + </div>
     85 + </div>
     86 + )}
     87 + 
     88 + {featuredSites && (
     89 + <div className={styles.sitesWrapper}>
     90 + <div className={styles.sites}>
     91 + <div className={styles.sitesList}>
     92 + {featuredSites.iconList.map((featuredSite) => (
     93 + <div key={featuredSite} className={styles.sitesItem}>
     94 + <img src={featuredSite} alt='' />
     95 + </div>
     96 + ))}
     97 + </div>
     98 + 
     99 + <div className={styles.sitesCount}>
     100 + + {numberWithSpaces(featuredSites.numberOfUses)} sites use
     101 + </div>
     102 + </div>
     103 + </div>
     104 + )}
     105 + 
     106 + {vulnerablePackage && (
     107 + <div className={styles.vulnerableWrapper}>
     108 + <span className={styles.vulnerablePackage}>
     109 + <span className={styles.vulnerableIcon}>
     110 + <Bug width='28' height='28' />
     111 + </span>
     112 + {vulnerablePackage.name}
     113 + </span>
     114 + {vulnerablePackage.moreCount && (
     115 + <span className={styles.vulnerableMore}>
     116 + <Chip variant='secondary' size='medium'>
     117 + +{vulnerablePackage.moreCount}{' '}
     118 + <span className={styles.vulnerableMoreText}>more</span>
     119 + </Chip>
     120 + </span>
     121 + )}
     122 + </div>
     123 + )}
     124 + </div>
     125 + );
     126 +}
     127 + 
  • ■ ■ ■ ■ ■ ■
    packages/web/src/components/ui/CardList/CardList.module.scss
     1 +@import '~styles/responsive.scss';
     2 + 
     3 +$mobile-gutter: 32px;
     4 + 
     5 +.grid {
     6 + display: grid;
     7 + grid-template-columns: repeat(3, minmax(0, 1fr));
     8 + grid-gap: 20px;
     9 + 
     10 + @include lg {
     11 + grid-template-columns: repeat(2, minmax(0, 1fr));
     12 + }
     13 + 
     14 + @include mobile-and-tablet {
     15 + padding-top: 35px;
     16 + padding-bottom: 52px;
     17 + margin-left: -24px;
     18 + margin-right: -24px;
     19 + display: grid;
     20 + grid-gap: $mobile-gutter / 2;
     21 + //grid-template-columns: 8px repeat(var(--total), calc(50% - #{$mobile-gutter} * 2)) 8px;
     22 + grid-template-columns: 8px repeat(var(--total), 312px) 8px;
     23 + grid-template-rows: minmax(204px, 1fr);
     24 + overflow-x: auto;
     25 + scroll-snap-type: x proximity;
     26 + 
     27 + &::before,
     28 + &::after {
     29 + content: '';
     30 + }
     31 + }
     32 +}
     33 + 
     34 +.vertical {
     35 + @include mobile-and-tablet {
     36 + grid-template-columns: auto;
     37 + grid-template-rows: minmax(136px, 1fr);
     38 + overflow: initial;
     39 + margin-left: 0;
     40 + margin-right: 0;
     41 + 
     42 + &::before,
     43 + &::after {
     44 + display: none;
     45 + }
     46 + }
     47 +}
     48 + 
  • ■ ■ ■ ■ ■ ■
    packages/web/src/components/ui/CardList/CardList.stories.tsx
     1 +import React from 'react';
     2 +import { ComponentMeta, ComponentStory } from '@storybook/react';
     3 +import Container from '../Container/Container';
     4 +import CardList from './CardList';
     5 + 
     6 +export default {
     7 + title: 'Interface / Card List',
     8 + component: CardList,
     9 + parameters: {
     10 + layout: 'fullscreen',
     11 + },
     12 +} as ComponentMeta<typeof CardList>;
     13 + 
     14 +const Template: ComponentStory<typeof CardList> = (args) => (
     15 + <Container>
     16 + <CardList {...args} />
     17 + </Container>
     18 +);
     19 + 
     20 +export const PopularSearchQueries = Template.bind({});
     21 +PopularSearchQueries.args = {
     22 + cards: [
     23 + {
     24 + title: 'github.com',
     25 + icon: 'https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg',
     26 + packageTags: {
     27 + featuredPackages: ['mdast-util-from-markdown', 'react', 'react-dom'],
     28 + restPackages: 45,
     29 + },
     30 + },
     31 + {
     32 + title: 'fingerprint.com',
     33 + icon: 'https://avatars.githubusercontent.com/u/67208791?s=200&v=4',
     34 + packageTags: {
     35 + featuredPackages: ['mdast-util-from-markdown', 'react', 'react-dom'],
     36 + restPackages: 45,
     37 + },
     38 + },
     39 + {
     40 + title: 'facebook.com',
     41 + icon: 'https://avatars.githubusercontent.com/u/69631?s=200&v=4',
     42 + packageTags: {
     43 + featuredPackages: ['react'],
     44 + restPackages: 45,
     45 + },
     46 + },
     47 + ],
     48 +};
     49 + 
     50 +export const PopularPackages = Template.bind({});
     51 +PopularPackages.args = {
     52 + cards: [
     53 + {
     54 + title: '@team-griffin/react-heading-section',
     55 + description: "This package's job is to automatically determine...",
     56 + featuredSites: {
     57 + iconList: [
     58 + 'https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg',
     59 + 'https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg',
     60 + 'https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg',
     61 + ],
     62 + numberOfUses: 5265,
     63 + },
     64 + },
     65 + {
     66 + title: 'unist-util-generated',
     67 + description: 'unist utility to check if a node is generated',
     68 + featuredSites: {
     69 + iconList: [
     70 + 'https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg',
     71 + 'https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg',
     72 + 'https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg',
     73 + ],
     74 + numberOfUses: 5265,
     75 + },
     76 + },
     77 + {
     78 + title: 'react-smooth',
     79 + description: 'is a animation library work on React',
     80 + featuredSites: {
     81 + iconList: [
     82 + 'https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg',
     83 + 'https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg',
     84 + 'https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg',
     85 + ],
     86 + numberOfUses: 5265,
     87 + },
     88 + },
     89 + {
     90 + title: 'unist-util-position',
     91 + description: 'unist utility to get the positional info of nodes',
     92 + featuredSites: {
     93 + iconList: [
     94 + 'https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg',
     95 + 'https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg',
     96 + 'https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg',
     97 + ],
     98 + numberOfUses: 5265,
     99 + },
     100 + },
     101 + {
     102 + title: 'vfile-message',
     103 + description: 'Create vfile messages',
     104 + featuredSites: {
     105 + iconList: [
     106 + 'https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg',
     107 + 'https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg',
     108 + 'https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg',
     109 + ],
     110 + numberOfUses: 5265,
     111 + },
     112 + },
     113 + {
     114 + title: 'Go to all Popular packages',
     115 + variant: 'toAll',
     116 + },
     117 + ],
     118 +};
     119 + 
     120 +export const VulnerableSites = Template.bind({});
     121 +VulnerableSites.args = {
     122 + variant: 'vertical',
     123 + cards: [
     124 + {
     125 + title: 'disneyland.omsk.ru/signup',
     126 + vulnerablePackage: {
     127 + name: 'mdast-util-from-markdown',
     128 + },
     129 + variant: 'vulnerable',
     130 + },
     131 + {
     132 + title: 'disneyland.omsk.ru/signup',
     133 + vulnerablePackage: {
     134 + name: 'mdast-util-from-markdown',
     135 + moreCount: 1,
     136 + },
     137 + variant: 'vulnerable',
     138 + },
     139 + {
     140 + title: 'disneyland.omsk.ru/signup',
     141 + vulnerablePackage: {
     142 + name: 'mdast-util-from-markdown',
     143 + },
     144 + variant: 'vulnerable',
     145 + },
     146 + ],
     147 +};
     148 + 
  • ■ ■ ■ ■ ■ ■
    packages/web/src/components/ui/CardList/CardList.tsx
     1 +import React from 'react';
     2 +import styles from './CardList.module.scss';
     3 +import Card, { CardProps } from '../Card/Card';
     4 +import clsx from 'clsx';
     5 + 
     6 +export type CardListProps = {
     7 + cards: CardProps[];
     8 + variant?: 'default' | 'vertical' | string;
     9 +};
     10 + 
     11 +export default function CardList({ cards, variant = 'default' }: CardListProps) {
     12 + const style = { '--total': cards.length } as React.CSSProperties;
     13 + 
     14 + return (
     15 + <div className={clsx(styles.grid, styles[variant])} style={style}>
     16 + {cards.map((card) => (
     17 + <Card key={card.title} {...card} />
     18 + ))}
     19 + </div>
     20 + );
     21 +}
     22 + 
  • ■ ■ ■ ■ ■ ■
    packages/web/src/components/ui/Chip/Chip.module.scss
     1 +@import '~styles/_vars.scss';
     2 +@import '~styles/responsive.scss';
     3 + 
     4 +.chip {
     5 + display: inline-flex;
     6 + align-items: center;
     7 + justify-content: center;
     8 + background-color: $gray-surface;
     9 + border-radius: 31px;
     10 + line-height: 1.25;
     11 + border: 1px solid transparent;
     12 + white-space: nowrap;
     13 + 
     14 + @include mobile-and-tablet {
     15 + font-size: 14px;
     16 + }
     17 +}
     18 + 
     19 +.secondary {
     20 + color: $gray-text;
     21 +}
     22 + 
     23 +.outlined {
     24 + border: 1px solid $gray-border;
     25 + background-color: transparent;
     26 +}
     27 + 
     28 +.suggest {
     29 + background: rgba($white, 0.1);
     30 + color: rgba($white, 0.8);
     31 +}
     32 + 
     33 +.regular {
     34 + padding: 4px 11px;
     35 +}
     36 + 
     37 +.medium {
     38 + padding: 7px 11px;
     39 +}
     40 + 
     41 +.large {
     42 + padding: 15px 19px;
     43 + 
     44 + @include mobile-and-tablet {
     45 + padding: 11px 15px;
     46 + }
     47 +}
     48 + 
     49 +.sans-serif {
     50 + font-size: $font-sans-serif;
     51 +}
     52 + 
     53 +.monospace {
     54 + font-family: $font-monospace;
     55 +}
     56 + 
  • ■ ■ ■ ■ ■ ■
    packages/web/src/components/ui/Chip/Chip.stories.tsx
     1 +import React from 'react';
     2 + 
     3 +import { ComponentStory, ComponentMeta } from '@storybook/react';
     4 + 
     5 +import Chip from './Chip';
     6 + 
     7 +export default {
     8 + title: 'Interface / Chip',
     9 + component: Chip,
     10 +} as ComponentMeta<typeof Chip>;
     11 + 
     12 +const Template: ComponentStory<typeof Chip> = (args) => <Chip {...args}>{args.children}</Chip>;
     13 + 
     14 +export const Regular = Template.bind({});
     15 +Regular.args = {
     16 + children: 'react',
     17 +};
     18 + 
     19 +export const Medium = Template.bind({});
     20 +Medium.args = {
     21 + children: 'react',
     22 + size: 'medium',
     23 +};
     24 + 
     25 +export const Large = Template.bind({});
     26 +Large.args = {
     27 + children: 'react',
     28 + size: 'large',
     29 +};
     30 + 
  • ■ ■ ■ ■ ■ ■
    packages/web/src/components/ui/Chip/Chip.tsx
     1 +import React from 'react';
     2 +import clsx from 'clsx';
     3 +import styles from './Chip.module.scss';
     4 + 
     5 +export type ChipProps = {
     6 + children: React.ReactNode;
     7 + variant?: 'primary' | 'secondary' | 'outlined' | 'suggest' | string;
     8 + size?: 'regular' | 'medium' | 'large' | string;
     9 + font?: 'sans-serif' | 'monospace' | string;
     10 +};
     11 + 
     12 +export default function Chip({
     13 + children,
     14 + variant = 'primary',
     15 + size = 'regular',
     16 + font = 'sans-serif',
     17 +}: ChipProps) {
     18 + return (
     19 + <span className={clsx(styles.chip, styles[variant], styles[size], styles[font])}>
     20 + {children}
     21 + </span>
     22 + );
     23 +}
     24 + 
  • ■ ■ ■ ■ ■ ■
    packages/web/src/components/ui/Container/Container.module.scss
     1 +.container {
     2 + width: 100%;
     3 + max-width: 1376px;
     4 + padding-left: 20px;
     5 + padding-right: 20px;
     6 + margin-left: auto;
     7 + margin-right: auto;
     8 +}
     9 + 
  • ■ ■ ■ ■ ■ ■
    packages/web/src/components/ui/Container/Container.tsx
     1 +import React from 'react';
     2 +import styles from './Container.module.scss';
     3 + 
     4 +export type ContainerProps = {
     5 + children: React.ReactNode;
     6 +};
     7 + 
     8 +export default function Container({ children }: ContainerProps) {
     9 + return <div className={styles.container}>{children}</div>;
     10 +}
     11 + 
  • ■ ■ ■ ■ ■ ■
    packages/web/src/components/ui/HeaderNew/HeaderNew.module.scss
     1 +@import '~styles/_vars.scss';
     2 +@import '~styles/responsive.scss';
     3 + 
     4 +.header {
     5 + display: flex;
     6 + align-items: center;
     7 + justify-content: space-between;
     8 + padding: 42px 0;
     9 + 
     10 + @include mobile-and-tablet {
     11 + padding: 22px 0;
     12 + }
     13 +}
     14 + 
     15 +.logo {
     16 + @include mobile-and-tablet {
     17 + width: 82px;
     18 + }
     19 +}
     20 + 
     21 +.nav {
     22 + display: flex;
     23 + align-items: center;
     24 + 
     25 + & > * + * {
     26 + margin-left: 40px;
     27 + 
     28 + @include mobile-and-tablet {
     29 + margin-left: 20px;
     30 + }
     31 + }
     32 +}
     33 + 
     34 +.navLink {
     35 + color: $white;
     36 +}
     37 + 
     38 +.githubIcon {
     39 + @include mobile-and-tablet {
     40 + width: 28px;
     41 + }
     42 +}
     43 + 
  • ■ ■ ■ ■ ■ ■
    packages/web/src/components/ui/HeaderNew/HeaderNew.tsx
     1 +import React from 'react';
     2 +import styles from './HeaderNew.module.scss';
     3 +import Container from '../Container/Container';
     4 +import { Github, Logo } from 'components/icons';
     5 +import { trackCustomEvent } from '../../../services/analytics';
     6 + 
     7 +export default function HeaderNew() {
     8 + return (
     9 + <Container>
     10 + <header className={styles.header}>
     11 + <Logo className={styles.logo} />
     12 + <div className={styles.nav}>
     13 + <a
     14 + href='https://github.com/gradejs/gradejs/discussions/6'
     15 + target='_blank'
     16 + rel='norefferer noreferrer'
     17 + className={styles.navLink}
     18 + onClick={() => trackCustomEvent('ClickExternalLink', 'About')}
     19 + >
     20 + About
     21 + </a>
     22 + <a
     23 + href='https://github.com/gradejs/gradejs/discussions'
     24 + target='_blank'
     25 + rel='norefferer noreferrer'
     26 + className={styles.navLink}
     27 + onClick={() => trackCustomEvent('ClickExternalLink', 'Community')}
     28 + >
     29 + Community
     30 + </a>
     31 + <a
     32 + href='https://github.com/gradejs/gradejs'
     33 + target='_blank'
     34 + rel='norefferer noreferrer'
     35 + className={styles.githubLink}
     36 + onClick={() => trackCustomEvent('ClickExternalLink', 'SourceCode')}
     37 + >
     38 + <Github className={styles.githubIcon} width='32' height='32' color='#fff' />
     39 + </a>
     40 + </div>
     41 + </header>
     42 + </Container>
     43 + );
     44 +}
     45 + 
  • ■ ■ ■ ■ ■ ■
    packages/web/src/components/ui/Hero/Hero.module.scss
     1 +@import '~styles/_vars.scss';
     2 +@import '~styles/responsive.scss';
     3 + 
     4 +.hero {
     5 + border-radius: 0 0 60px 60px;
     6 + background: url('~assets/hero-bg.png') 50% 100% no-repeat;
     7 + background-size: cover;
     8 + color: $white;
     9 + text-align: center;
     10 +}
     11 + 
     12 +.content {
     13 + padding-top: 24px;
     14 + padding-bottom: 100px;
     15 + 
     16 + @include mobile-and-tablet {
     17 + padding-top: 20px;
     18 + padding-bottom: 44px;
     19 + }
     20 +}
     21 + 
     22 +.title {
     23 + max-width: 884px;
     24 + margin-left: auto;
     25 + margin-right: auto;
     26 + 
     27 + @include mobile-and-tablet {
     28 + margin-bottom: 20px;
     29 + }
     30 +}
     31 + 
     32 +.subtitle {
     33 + max-width: 616px;
     34 + margin-left: auto;
     35 + margin-right: auto;
     36 + color: rgba($white, 0.5);
     37 + margin-bottom: 48px;
     38 + 
     39 + @include mobile-and-tablet {
     40 + margin-bottom: 32px;
     41 + }
     42 +}
     43 + 
     44 +.search {
     45 + position: relative;
     46 + max-width: 758px;
     47 + margin-left: auto;
     48 + margin-right: auto;
     49 +}
     50 + 
     51 +.input {
     52 + display: block;
     53 + width: 100%;
     54 + height: 100px;
     55 + padding-left: 44px;
     56 + padding-right: 124px;
     57 + font-size: 26px;
     58 + font-family: inherit;
     59 + background: rgba($white, 0.16);
     60 + border: none;
     61 + outline: none;
     62 + // FIXME: not sure that non-empty input should have transparent text like placeholder
     63 + color: rgba($white, 0.5);
     64 + //color: $white;
     65 + border-radius: 50px;
     66 + 
     67 + @include mobile-and-tablet {
     68 + height: 64px;
     69 + padding-left: 28px;
     70 + padding-right: 92px;
     71 + font-size: 20px;
     72 + }
     73 + 
     74 + &::placeholder {
     75 + color: $white;
     76 + opacity: 0.5;
     77 + }
     78 +}
     79 + 
     80 +.submit {
     81 + cursor: pointer;
     82 + flex-shrink: 0;
     83 + position: absolute;
     84 + z-index: 1;
     85 + top: 8px;
     86 + right: 8px;
     87 + width: 84px;
     88 + height: 84px;
     89 + border-radius: 50%;
     90 + display: flex;
     91 + align-items: center;
     92 + justify-content: center;
     93 + border: none;
     94 + margin: 0;
     95 + padding: 0;
     96 + background-color: rgba($white, 0.1);
     97 + 
     98 + @include mobile-and-tablet {
     99 + top: 4px;
     100 + right: 4px;
     101 + width: 56px;
     102 + height: 56px;
     103 + }
     104 +}
     105 + 
     106 +.submitIcon {
     107 + margin-left: 4px;
     108 + 
     109 + @include mobile-and-tablet {
     110 + width: 20px;
     111 + height: 20px;
     112 + }
     113 +}
     114 + 
     115 +.suggestions {
     116 + margin-top: 28px;
     117 + display: flex;
     118 + align-items: center;
     119 + justify-content: center;
     120 + 
     121 + @include mobile-and-tablet {
     122 + margin-top: 24px;
     123 + }
     124 + 
     125 + & > * {
     126 + margin: 0 4px;
     127 + 
     128 + @include mobile-and-tablet {
     129 + &:nth-child(n + 4) {
     130 + display: none;
     131 + }
     132 + }
     133 + }
     134 +}
     135 + 
     136 +.loader {
     137 + width: 32px;
     138 + height: 32px;
     139 + border: 4px solid #fff;
     140 + border-bottom-color: transparent;
     141 + border-radius: 50%;
     142 + display: inline-block;
     143 + box-sizing: border-box;
     144 + animation: rotation 1s linear infinite;
     145 +}
     146 + 
     147 +@keyframes rotation {
     148 + 0% {
     149 + transform: rotate(0deg);
     150 + }
     151 + 100% {
     152 + transform: rotate(360deg);
     153 + }
     154 +}
     155 + 
  • ■ ■ ■ ■ ■ ■
    packages/web/src/components/ui/Hero/Hero.stories.tsx
     1 +import React from 'react';
     2 +import { ComponentMeta, ComponentStory } from '@storybook/react';
     3 + 
     4 +import Hero from './Hero';
     5 + 
     6 +export default {
     7 + title: 'Interface / Hero',
     8 + component: Hero,
     9 + parameters: {
     10 + layout: 'fullscreen',
     11 + },
     12 +} as ComponentMeta<typeof Hero>;
     13 + 
     14 +const Template: ComponentStory<typeof Hero> = (args) => <Hero {...args} />;
     15 + 
     16 +export const Main = Template.bind({});
     17 +Main.args = {
     18 + suggestions: ['tinkoff.ru', 'pinterest.com', 'github.com', 'gradejs.com', 'npmjs.com'],
     19 +};
     20 + 
     21 +export const Loading = Template.bind({});
     22 +Loading.args = {
     23 + inputText:
     24 + 'https://github.com/remix-run/react-router/blob/main/packages/react-router/index.ts#L85',
     25 + loading: true,
     26 + suggestions: ['tinkoff.ru', 'pinterest.com', 'github.com', 'gradejs.com', 'npmjs.com'],
     27 +};
     28 + 
  • ■ ■ ■ ■ ■ ■
    packages/web/src/components/ui/Hero/Hero.tsx
     1 +import React from 'react';
     2 +import styles from './Hero.module.scss';
     3 +import Container from '../Container/Container';
     4 +import Chip from '../Chip/Chip';
     5 +import { Arrow } from '../../icons';
     6 +import HeaderNew from '../HeaderNew/HeaderNew';
     7 + 
     8 +export type HeroProps = {
     9 + inputText?: string;
     10 + loading?: boolean;
     11 + suggestions: string[];
     12 +};
     13 + 
     14 +export default function Hero({ inputText, suggestions, loading = false }: HeroProps) {
     15 + return (
     16 + <section className={styles.hero}>
     17 + <HeaderNew />
     18 + 
     19 + <Container>
     20 + <div className={styles.content}>
     21 + <h1 className={styles.title}>Analyze webpack production bundle</h1>
     22 + <p className={styles.subtitle}>
     23 + GradeJS will analyze production JavaScript files and match webpack bundled modules to
     24 + 1,826 indexed NPM libraries over 54,735 releases
     25 + </p>
     26 + 
     27 + <div className={styles.search}>
     28 + <input
     29 + type='text'
     30 + className={styles.input}
     31 + value={inputText}
     32 + placeholder='Start analyzing...'
     33 + />
     34 + <button type='submit' className={styles.submit}>
     35 + {/* TODO: use SVG loading component */}
     36 + {!loading ? (
     37 + <Arrow className={styles.submitIcon} width='32' height='32' color='#fff' />
     38 + ) : (
     39 + <span className={styles.loader} />
     40 + )}
     41 + </button>
     42 + </div>
     43 + 
     44 + <div className={styles.suggestions}>
     45 + {suggestions.map((suggestion) => (
     46 + <Chip key={suggestion} variant='suggest' size='medium'>
     47 + {suggestion}
     48 + </Chip>
     49 + ))}
     50 + </div>
     51 + </div>
     52 + </Container>
     53 + </section>
     54 + );
     55 +}
     56 + 
  • ■ ■ ■ ■ ■ ■
    packages/web/src/styles/_fonts.scss
     1 +@mixin font($font-family, $file-path, $weight: normal, $style: normal) {
     2 + @font-face {
     3 + font-family: $font-family;
     4 + font-weight: $weight;
     5 + font-style: $style;
     6 + font-display: swap;
     7 + src: url('#{$file-path}.woff2') format('woff2'), url('#{$file-path}.woff') format('woff');
     8 + }
     9 +}
     10 + 
     11 +@include font('Onest', '~assets/fonts/Onest/OnestThin', 100);
     12 +@include font('Onest', '~assets/fonts/Onest/OnestLight', 300);
     13 +@include font('Onest', '~assets/fonts/Onest/OnestRegular');
     14 +@include font('Onest', '~assets/fonts/Onest/OnestMedium', 500);
     15 +@include font('Onest', '~assets/fonts/Onest/OnestBold', 700);
     16 +@include font('Onest', '~assets/fonts/Onest/OnestExtraBold', 800);
     17 +@include font('Onest', '~assets/fonts/Onest/OnestBlack', 900);
     18 + 
     19 +@include font('Roboto', '~assets/fonts/RobotoMono/RobotoMono-Regular');
     20 +@include font('Roboto', '~assets/fonts/RobotoMono/RobotoMono-Medium', 500);
     21 + 
  • ■ ■ ■ ■ ■ ■
    packages/web/src/styles/_vars.scss
     1 +$system-sans-serif: system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', 'Noto Sans',
     2 + 'Liberation Sans', Arial, sans-serif;
     3 +$font-sans-serif: 'Onest', $system-sans-serif;
     4 + 
     5 +$system-monospace: SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New',
     6 + monospace;
     7 +$font-monospace: 'Roboto', $system-monospace;
     8 + 
     9 +$blue-accent: #4549ff;
     10 +$blue-secondary: #a2a4ff;
     11 +$red-accent: #f3512e;
     12 +$red-secondary: #f9a896;
     13 +$orange-accent: #f3812e;
     14 +$orange-secondary: #f9bf96;
     15 +$yellow-accent: #f1ce61;
     16 +$yellow-secondary: #f8e6af;
     17 +$green-accent: #49d581;
     18 +$green-secondary: #a4eabf;
     19 + 
     20 +$black: #212121;
     21 +$gray-text: #8e8aa0;
     22 +$gray-border: #dddce3;
     23 +$gray-surface: #f7f7f9;
     24 +$white: #fff;
     25 + 
     26 +$shadow: 0px 1px 35px rgba(#5c54ba, 0.1);
     27 + 
  • ■ ■ ■ ■ ■ ■
    packages/web/src/styles/global.scss
    1  -@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap');
     1 +@import './_vars.scss';
     2 +@import './_fonts.scss';
    2 3  @import './responsive.scss';
    3 4   
     5 +*,
     6 +::after,
     7 +::before {
     8 + box-sizing: border-box;
     9 +}
     10 + 
    4 11  body {
    5  - font-family: 'Inter', sans-serif;
     12 + margin: 0;
     13 + font-family: $font-sans-serif;
     14 + font-size: 1rem;
     15 + line-height: 1.62;
     16 + color: $black;
     17 + text-align: left;
     18 + background-color: $white;
    6 19   -webkit-font-smoothing: antialiased;
     20 + -moz-osx-font-smoothing: grayscale;
     21 + 
     22 + @include mobile-and-tablet {
     23 + font-size: 0.875rem;
     24 + line-height: 1.57;
     25 + }
     26 +}
     27 + 
     28 +code,
     29 +kbd,
     30 +pre,
     31 +samp {
     32 + font-family: $font-monospace;
     33 +}
     34 + 
     35 +h1,
     36 +h2,
     37 +h3,
     38 +h4,
     39 +h5,
     40 +h6 {
     41 + margin-top: 0;
     42 + margin-bottom: 24px;
    7 43  }
    8 44   
    9 45  h1 {
    10  - font-size: 100px;
    11  - line-height: 100%;
    12  - margin: 0 0 80px;
     46 + font-size: 82px;
     47 + line-height: 92px;
    13 48   
    14  - @include mobile {
    15  - font-size: 60px;
    16  - margin-bottom: 64px;
     49 + @include mobile-and-tablet {
     50 + font-size: 36px;
     51 + line-height: 46px;
    17 52   }
     53 +}
     54 + 
     55 +h2 {
     56 + font-size: 48px;
     57 + line-height: 58px;
     58 + 
     59 + @include mobile-and-tablet {
     60 + font-size: 28px;
     61 + line-height: 36px;
     62 + }
     63 +}
     64 + 
     65 +h3 {
     66 + font-size: 40px;
     67 + line-height: 50px;
     68 + 
     69 + @include mobile-and-tablet {
     70 + font-size: 24px;
     71 + line-height: 30px;
     72 + }
     73 +}
     74 + 
     75 +h4 {
     76 + font-size: 32px;
     77 + line-height: 50px;
     78 + 
     79 + @include mobile-and-tablet {
     80 + font-size: 22px;
     81 + line-height: 30px;
     82 + }
     83 +}
     84 + 
     85 +h5 {
     86 + font-size: 26px;
     87 + line-height: 40px;
     88 +}
     89 + 
     90 +h6 {
     91 + font-size: 19px;
     92 + line-height: 30px;
     93 +}
     94 + 
     95 +p {
     96 + margin-top: 0;
     97 +}
     98 + 
     99 +a {
     100 + color: $blue-accent;
     101 + text-decoration: none;
    18 102  }
    19 103   
    20 104  #root,
    skipped 6 lines
  • ■ ■ ■ ■ ■
    packages/web/src/styles/responsive.scss
    1  -$breakpoint-mobile: 415px;
    2  -$breakpoint-tablet: 768px;
     1 +$breakpoint-mobile: 575px;
     2 +$breakpoint-tablet: 991px;
     3 +$breakpoint-lg: 1199px;
     4 +$breakpoint-xl: 1399px;
     5 + 
     6 +@mixin xl {
     7 + @media (max-width: $breakpoint-xl) {
     8 + @content;
     9 + }
     10 +}
     11 + 
     12 +@mixin lg {
     13 + @media (max-width: $breakpoint-lg) {
     14 + @content;
     15 + }
     16 +}
    3 17   
    4 18  @mixin mobile-and-tablet {
    5 19   @media (max-width: $breakpoint-tablet) {
    6 20   @content;
    7 21   }
    8 22  }
     23 + 
    9 24  @mixin mobile {
    10 25   @media (max-width: $breakpoint-mobile) {
    11 26   @content;
    skipped 3 lines
  • ■ ■ ■ ■ ■ ■
    packages/web/src/utils/helpers.tsx
     1 +export function numberWithSpaces(x: number) {
     2 + return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ' ');
     3 +}
     4 + 
Please wait...
Page is in error, reload to recover