diff --git a/demo/figma/attached-file.html b/demo/figma/attached-file.html index 5b936c5..fb71b0c 100644 --- a/demo/figma/attached-file.html +++ b/demo/figma/attached-file.html @@ -14,26 +14,26 @@ #figma-attached-file { width: 560px; height: 2000px; - padding: 70px; + padding: 80px; box-sizing: border-box; display: flex; flex-direction: column; + align-items: center; + gap: 80px; background: #000000; } #figma-attached-file .file-attachment, #figma-attached-file .file-upload-preview-item { - flex: 1 1 0; - width: 100%; - max-width: none; - height: auto; - aspect-ratio: auto; + flex: 0 0 auto; + width: 400px; + height: 400px; border-radius: 40px; }
-
+
PDF @@ -44,7 +44,7 @@
-
+
JPG @@ -60,7 +60,11 @@ DOCX
-
+
+ + + +
Upload failed.docx
@@ -71,7 +75,11 @@ ZIP
-
+
+ + + +
Retry archive.zip
diff --git a/demo/figma/breadcrumbs.html b/demo/figma/breadcrumbs.html index 2d6b445..e1fe75d 100644 --- a/demo/figma/breadcrumbs.html +++ b/demo/figma/breadcrumbs.html @@ -14,7 +14,7 @@ #figma-breadcrumbs { width: 1200px; height: 567px; - padding: 40px; + padding: 47px 50px 0; box-sizing: border-box; overflow: hidden; background: #000000; @@ -23,6 +23,7 @@ display: flex; justify-content: space-between; align-items: center; + height: 83px; margin-bottom: 48px; } .figma-row:last-child { margin-bottom: 0; } diff --git a/demo/partials/toasts.html b/demo/partials/toasts.html index db5e9ab..e57beb0 100644 --- a/demo/partials/toasts.html +++ b/demo/partials/toasts.html @@ -2,16 +2,16 @@

Toasts

- - - -
diff --git a/package-lock.json b/package-lock.json index cf471fd..af8f620 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.1.0", "license": "UNLICENSED", "devDependencies": { + "@phosphor-icons/web": "^2.1.2", "@playwright/test": "^1.60.0", "autoprefixer": "^10.4.27", "browser-sync": "^3.0.4", @@ -1402,6 +1403,13 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/@phosphor-icons/web": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@phosphor-icons/web/-/web-2.1.2.tgz", + "integrity": "sha512-rPAR9o/bEcp4Cw4DEeZHXf+nlGCMNGkNDRizYHM47NLxz9vvEHp/Tt6FMK1NcWadzw/pFDPnRBGi/ofRya958A==", + "dev": true, + "license": "MIT" + }, "node_modules/@playwright/test": { "version": "1.62.1", "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.62.1.tgz", diff --git a/package.json b/package.json index 0740655..0d030d1 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,7 @@ } }, "devDependencies": { + "@phosphor-icons/web": "^2.1.2", "@playwright/test": "^1.60.0", "autoprefixer": "^10.4.27", "browser-sync": "^3.0.4", diff --git a/public/assets/fonts/phosphor-icons/src/css/Phosphor.svg b/public/assets/fonts/phosphor-icons/src/css/Phosphor.svg new file mode 100644 index 0000000..8e62676 --- /dev/null +++ b/public/assets/fonts/phosphor-icons/src/css/Phosphor.svg @@ -0,0 +1,3056 @@ + + + + + + +{ + "fontFamily": "Phosphor", + "majorVersion": 2, + "minorVersion": 1, + "fontURL": "https://phosphoricons.com", + "description": "A flexible icon family for everyone\nFont generated by IcoMoon.", + "copyright": "Phosphor Icons", + "designer": "Tobias Fried & Helena Zhang", + "designerURL": "https://phosphoricons.com", + "license": "MIT", + "licenseURL": "https://raw.githubusercontent.com/phosphor-icons/homepage/master/LICENSE", + "version": "Version 2.1", + "fontId": "Phosphor", + "psName": "Phosphor", + "subFamily": "Regular", + "fullName": "Phosphor" +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/assets/fonts/phosphor-icons/src/css/Phosphor.ttf b/public/assets/fonts/phosphor-icons/src/css/Phosphor.ttf new file mode 100644 index 0000000..7c1b8a7 --- /dev/null +++ b/public/assets/fonts/phosphor-icons/src/css/Phosphor.ttf Binary files differ diff --git a/public/assets/fonts/phosphor-icons/src/css/Phosphor.woff b/public/assets/fonts/phosphor-icons/src/css/Phosphor.woff new file mode 100644 index 0000000..c48057d --- /dev/null +++ b/public/assets/fonts/phosphor-icons/src/css/Phosphor.woff Binary files differ diff --git a/public/assets/fonts/phosphor-icons/src/css/Phosphor.woff2 b/public/assets/fonts/phosphor-icons/src/css/Phosphor.woff2 new file mode 100644 index 0000000..76ff203 --- /dev/null +++ b/public/assets/fonts/phosphor-icons/src/css/Phosphor.woff2 Binary files differ diff --git a/public/assets/fonts/phosphor-icons/src/css/icons.css b/public/assets/fonts/phosphor-icons/src/css/icons.css index 8ac907c..333f23c 100644 --- a/public/assets/fonts/phosphor-icons/src/css/icons.css +++ b/public/assets/fonts/phosphor-icons/src/css/icons.css @@ -1,11 +1,4627 @@ -/* Phosphor Icons placeholder. - Replace with the real Phosphor icon font CSS when assets are available. */ -.ph { - display: inline-block; +@font-face { + font-family: "Phosphor"; + src: + url("./Phosphor.woff2") format("woff2"), + url("./Phosphor.woff") format("woff"), + url("./Phosphor.ttf") format("truetype"), + url("./Phosphor.svg#Phosphor") format("svg"); + font-weight: normal; font-style: normal; + font-display: block; +} + +.ph { + /* use !important to prevent issues with browser extensions that change fonts */ + font-family: "Phosphor" !important; + speak: never; + font-style: normal; + font-weight: normal; font-variant: normal; - text-rendering: auto; + text-transform: none; line-height: 1; + + /* Enable Ligatures ================ */ + letter-spacing: 0; + -webkit-font-feature-settings: "liga"; + -moz-font-feature-settings: "liga=1"; + -moz-font-feature-settings: "liga"; + -ms-font-feature-settings: "liga" 1; + font-feature-settings: "liga"; + -webkit-font-variant-ligatures: discretionary-ligatures; + font-variant-ligatures: discretionary-ligatures; + + /* Better Font Rendering =========== */ -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } + +.ph.ph-acorn:before { + content: "\eb9a"; +} +.ph.ph-address-book:before { + content: "\e6f8"; +} +.ph.ph-address-book-tabs:before { + content: "\ee4e"; +} +.ph.ph-air-traffic-control:before { + content: "\ecd8"; +} +.ph.ph-airplane:before { + content: "\e002"; +} +.ph.ph-airplane-in-flight:before { + content: "\e4fe"; +} +.ph.ph-airplane-landing:before { + content: "\e502"; +} +.ph.ph-airplane-takeoff:before { + content: "\e504"; +} +.ph.ph-airplane-taxiing:before { + content: "\e500"; +} +.ph.ph-airplane-tilt:before { + content: "\e5d6"; +} +.ph.ph-airplay:before { + content: "\e004"; +} +.ph.ph-alarm:before { + content: "\e006"; +} +.ph.ph-alien:before { + content: "\e8a6"; +} +.ph.ph-align-bottom:before { + content: "\e506"; +} +.ph.ph-align-bottom-simple:before { + content: "\eb0c"; +} +.ph.ph-align-center-horizontal:before { + content: "\e50a"; +} +.ph.ph-align-center-horizontal-simple:before { + content: "\eb0e"; +} +.ph.ph-align-center-vertical:before { + content: "\e50c"; +} +.ph.ph-align-center-vertical-simple:before { + content: "\eb10"; +} +.ph.ph-align-left:before { + content: "\e50e"; +} +.ph.ph-align-left-simple:before { + content: "\eaee"; +} +.ph.ph-align-right:before { + content: "\e510"; +} +.ph.ph-align-right-simple:before { + content: "\eb12"; +} +.ph.ph-align-top:before { + content: "\e512"; +} +.ph.ph-align-top-simple:before { + content: "\eb14"; +} +.ph.ph-amazon-logo:before { + content: "\e96c"; +} +.ph.ph-ambulance:before { + content: "\e572"; +} +.ph.ph-anchor:before { + content: "\e514"; +} +.ph.ph-anchor-simple:before { + content: "\e5d8"; +} +.ph.ph-android-logo:before { + content: "\e008"; +} +.ph.ph-angle:before { + content: "\e7bc"; +} +.ph.ph-angular-logo:before { + content: "\eb80"; +} +.ph.ph-aperture:before { + content: "\e00a"; +} +.ph.ph-app-store-logo:before { + content: "\e974"; +} +.ph.ph-app-window:before { + content: "\e5da"; +} +.ph.ph-apple-logo:before { + content: "\e516"; +} +.ph.ph-apple-podcasts-logo:before { + content: "\eb96"; +} +.ph.ph-approximate-equals:before { + content: "\edaa"; +} +.ph.ph-archive:before { + content: "\e00c"; +} +.ph.ph-armchair:before { + content: "\e012"; +} +.ph.ph-arrow-arc-left:before { + content: "\e014"; +} +.ph.ph-arrow-arc-right:before { + content: "\e016"; +} +.ph.ph-arrow-bend-double-up-left:before { + content: "\e03a"; +} +.ph.ph-arrow-bend-double-up-right:before { + content: "\e03c"; +} +.ph.ph-arrow-bend-down-left:before { + content: "\e018"; +} +.ph.ph-arrow-bend-down-right:before { + content: "\e01a"; +} +.ph.ph-arrow-bend-left-down:before { + content: "\e01c"; +} +.ph.ph-arrow-bend-left-up:before { + content: "\e01e"; +} +.ph.ph-arrow-bend-right-down:before { + content: "\e020"; +} +.ph.ph-arrow-bend-right-up:before { + content: "\e022"; +} +.ph.ph-arrow-bend-up-left:before { + content: "\e024"; +} +.ph.ph-arrow-bend-up-right:before { + content: "\e026"; +} +.ph.ph-arrow-circle-down:before { + content: "\e028"; +} +.ph.ph-arrow-circle-down-left:before { + content: "\e02a"; +} +.ph.ph-arrow-circle-down-right:before { + content: "\e02c"; +} +.ph.ph-arrow-circle-left:before { + content: "\e05a"; +} +.ph.ph-arrow-circle-right:before { + content: "\e02e"; +} +.ph.ph-arrow-circle-up:before { + content: "\e030"; +} +.ph.ph-arrow-circle-up-left:before { + content: "\e032"; +} +.ph.ph-arrow-circle-up-right:before { + content: "\e034"; +} +.ph.ph-arrow-clockwise:before { + content: "\e036"; +} +.ph.ph-arrow-counter-clockwise:before { + content: "\e038"; +} +.ph.ph-arrow-down:before { + content: "\e03e"; +} +.ph.ph-arrow-down-left:before { + content: "\e040"; +} +.ph.ph-arrow-down-right:before { + content: "\e042"; +} +.ph.ph-arrow-elbow-down-left:before { + content: "\e044"; +} +.ph.ph-arrow-elbow-down-right:before { + content: "\e046"; +} +.ph.ph-arrow-elbow-left:before { + content: "\e048"; +} +.ph.ph-arrow-elbow-left-down:before { + content: "\e04a"; +} +.ph.ph-arrow-elbow-left-up:before { + content: "\e04c"; +} +.ph.ph-arrow-elbow-right:before { + content: "\e04e"; +} +.ph.ph-arrow-elbow-right-down:before { + content: "\e050"; +} +.ph.ph-arrow-elbow-right-up:before { + content: "\e052"; +} +.ph.ph-arrow-elbow-up-left:before { + content: "\e054"; +} +.ph.ph-arrow-elbow-up-right:before { + content: "\e056"; +} +.ph.ph-arrow-fat-down:before { + content: "\e518"; +} +.ph.ph-arrow-fat-left:before { + content: "\e51a"; +} +.ph.ph-arrow-fat-line-down:before { + content: "\e51c"; +} +.ph.ph-arrow-fat-line-left:before { + content: "\e51e"; +} +.ph.ph-arrow-fat-line-right:before { + content: "\e520"; +} +.ph.ph-arrow-fat-line-up:before { + content: "\e522"; +} +.ph.ph-arrow-fat-lines-down:before { + content: "\e524"; +} +.ph.ph-arrow-fat-lines-left:before { + content: "\e526"; +} +.ph.ph-arrow-fat-lines-right:before { + content: "\e528"; +} +.ph.ph-arrow-fat-lines-up:before { + content: "\e52a"; +} +.ph.ph-arrow-fat-right:before { + content: "\e52c"; +} +.ph.ph-arrow-fat-up:before { + content: "\e52e"; +} +.ph.ph-arrow-left:before { + content: "\e058"; +} +.ph.ph-arrow-line-down:before { + content: "\e05c"; +} +.ph.ph-arrow-line-down-left:before { + content: "\e05e"; +} +.ph.ph-arrow-line-down-right:before { + content: "\e060"; +} +.ph.ph-arrow-line-left:before { + content: "\e062"; +} +.ph.ph-arrow-line-right:before { + content: "\e064"; +} +.ph.ph-arrow-line-up:before { + content: "\e066"; +} +.ph.ph-arrow-line-up-left:before { + content: "\e068"; +} +.ph.ph-arrow-line-up-right:before { + content: "\e06a"; +} +.ph.ph-arrow-right:before { + content: "\e06c"; +} +.ph.ph-arrow-square-down:before { + content: "\e06e"; +} +.ph.ph-arrow-square-down-left:before { + content: "\e070"; +} +.ph.ph-arrow-square-down-right:before { + content: "\e072"; +} +.ph.ph-arrow-square-in:before { + content: "\e5dc"; +} +.ph.ph-arrow-square-left:before { + content: "\e074"; +} +.ph.ph-arrow-square-out:before { + content: "\e5de"; +} +.ph.ph-arrow-square-right:before { + content: "\e076"; +} +.ph.ph-arrow-square-up:before { + content: "\e078"; +} +.ph.ph-arrow-square-up-left:before { + content: "\e07a"; +} +.ph.ph-arrow-square-up-right:before { + content: "\e07c"; +} +.ph.ph-arrow-u-down-left:before { + content: "\e07e"; +} +.ph.ph-arrow-u-down-right:before { + content: "\e080"; +} +.ph.ph-arrow-u-left-down:before { + content: "\e082"; +} +.ph.ph-arrow-u-left-up:before { + content: "\e084"; +} +.ph.ph-arrow-u-right-down:before { + content: "\e086"; +} +.ph.ph-arrow-u-right-up:before { + content: "\e088"; +} +.ph.ph-arrow-u-up-left:before { + content: "\e08a"; +} +.ph.ph-arrow-u-up-right:before { + content: "\e08c"; +} +.ph.ph-arrow-up:before { + content: "\e08e"; +} +.ph.ph-arrow-up-left:before { + content: "\e090"; +} +.ph.ph-arrow-up-right:before { + content: "\e092"; +} +.ph.ph-arrows-clockwise:before { + content: "\e094"; +} +.ph.ph-arrows-counter-clockwise:before { + content: "\e096"; +} +.ph.ph-arrows-down-up:before { + content: "\e098"; +} +.ph.ph-arrows-horizontal:before { + content: "\eb06"; +} +.ph.ph-arrows-in:before { + content: "\e09a"; +} +.ph.ph-arrows-in-cardinal:before { + content: "\e09c"; +} +.ph.ph-arrows-in-line-horizontal:before { + content: "\e530"; +} +.ph.ph-arrows-in-line-vertical:before { + content: "\e532"; +} +.ph.ph-arrows-in-simple:before { + content: "\e09e"; +} +.ph.ph-arrows-left-right:before { + content: "\e0a0"; +} +.ph.ph-arrows-merge:before { + content: "\ed3e"; +} +.ph.ph-arrows-out:before { + content: "\e0a2"; +} +.ph.ph-arrows-out-cardinal:before { + content: "\e0a4"; +} +.ph.ph-arrows-out-line-horizontal:before { + content: "\e534"; +} +.ph.ph-arrows-out-line-vertical:before { + content: "\e536"; +} +.ph.ph-arrows-out-simple:before { + content: "\e0a6"; +} +.ph.ph-arrows-split:before { + content: "\ed3c"; +} +.ph.ph-arrows-vertical:before { + content: "\eb04"; +} +.ph.ph-article:before { + content: "\e0a8"; +} +.ph.ph-article-medium:before { + content: "\e5e0"; +} +.ph.ph-article-ny-times:before { + content: "\e5e2"; +} +.ph.ph-asclepius:before { + content: "\ee34"; +} +.ph.ph-caduceus:before { + content: "\ee34"; +} +.ph.ph-asterisk:before { + content: "\e0aa"; +} +.ph.ph-asterisk-simple:before { + content: "\e832"; +} +.ph.ph-at:before { + content: "\e0ac"; +} +.ph.ph-atom:before { + content: "\e5e4"; +} +.ph.ph-avocado:before { + content: "\ee04"; +} +.ph.ph-axe:before { + content: "\e9fc"; +} +.ph.ph-baby:before { + content: "\e774"; +} +.ph.ph-baby-carriage:before { + content: "\e818"; +} +.ph.ph-backpack:before { + content: "\e922"; +} +.ph.ph-backspace:before { + content: "\e0ae"; +} +.ph.ph-bag:before { + content: "\e0b0"; +} +.ph.ph-bag-simple:before { + content: "\e5e6"; +} +.ph.ph-balloon:before { + content: "\e76c"; +} +.ph.ph-bandaids:before { + content: "\e0b2"; +} +.ph.ph-bank:before { + content: "\e0b4"; +} +.ph.ph-barbell:before { + content: "\e0b6"; +} +.ph.ph-barcode:before { + content: "\e0b8"; +} +.ph.ph-barn:before { + content: "\ec72"; +} +.ph.ph-barricade:before { + content: "\e948"; +} +.ph.ph-baseball:before { + content: "\e71a"; +} +.ph.ph-baseball-cap:before { + content: "\ea28"; +} +.ph.ph-baseball-helmet:before { + content: "\ee4a"; +} +.ph.ph-basket:before { + content: "\e964"; +} +.ph.ph-basketball:before { + content: "\e724"; +} +.ph.ph-bathtub:before { + content: "\e81e"; +} +.ph.ph-battery-charging:before { + content: "\e0ba"; +} +.ph.ph-battery-charging-vertical:before { + content: "\e0bc"; +} +.ph.ph-battery-empty:before { + content: "\e0be"; +} +.ph.ph-battery-full:before { + content: "\e0c0"; +} +.ph.ph-battery-high:before { + content: "\e0c2"; +} +.ph.ph-battery-low:before { + content: "\e0c4"; +} +.ph.ph-battery-medium:before { + content: "\e0c6"; +} +.ph.ph-battery-plus:before { + content: "\e808"; +} +.ph.ph-battery-plus-vertical:before { + content: "\ec50"; +} +.ph.ph-battery-vertical-empty:before { + content: "\e7c6"; +} +.ph.ph-battery-vertical-full:before { + content: "\e7c4"; +} +.ph.ph-battery-vertical-high:before { + content: "\e7c2"; +} +.ph.ph-battery-vertical-low:before { + content: "\e7be"; +} +.ph.ph-battery-vertical-medium:before { + content: "\e7c0"; +} +.ph.ph-battery-warning:before { + content: "\e0c8"; +} +.ph.ph-battery-warning-vertical:before { + content: "\e0ca"; +} +.ph.ph-beach-ball:before { + content: "\ed24"; +} +.ph.ph-beanie:before { + content: "\ea2a"; +} +.ph.ph-bed:before { + content: "\e0cc"; +} +.ph.ph-beer-bottle:before { + content: "\e7b0"; +} +.ph.ph-beer-stein:before { + content: "\eb62"; +} +.ph.ph-behance-logo:before { + content: "\e7f4"; +} +.ph.ph-bell:before { + content: "\e0ce"; +} +.ph.ph-bell-ringing:before { + content: "\e5e8"; +} +.ph.ph-bell-simple:before { + content: "\e0d0"; +} +.ph.ph-bell-simple-ringing:before { + content: "\e5ea"; +} +.ph.ph-bell-simple-slash:before { + content: "\e0d2"; +} +.ph.ph-bell-simple-z:before { + content: "\e5ec"; +} +.ph.ph-bell-slash:before { + content: "\e0d4"; +} +.ph.ph-bell-z:before { + content: "\e5ee"; +} +.ph.ph-belt:before { + content: "\ea2c"; +} +.ph.ph-bezier-curve:before { + content: "\eb00"; +} +.ph.ph-bicycle:before { + content: "\e0d6"; +} +.ph.ph-binary:before { + content: "\ee60"; +} +.ph.ph-binoculars:before { + content: "\ea64"; +} +.ph.ph-biohazard:before { + content: "\e9e0"; +} +.ph.ph-bird:before { + content: "\e72c"; +} +.ph.ph-blueprint:before { + content: "\eda0"; +} +.ph.ph-bluetooth:before { + content: "\e0da"; +} +.ph.ph-bluetooth-connected:before { + content: "\e0dc"; +} +.ph.ph-bluetooth-slash:before { + content: "\e0de"; +} +.ph.ph-bluetooth-x:before { + content: "\e0e0"; +} +.ph.ph-boat:before { + content: "\e786"; +} +.ph.ph-bomb:before { + content: "\ee0a"; +} +.ph.ph-bone:before { + content: "\e7f2"; +} +.ph.ph-book:before { + content: "\e0e2"; +} +.ph.ph-book-bookmark:before { + content: "\e0e4"; +} +.ph.ph-book-open:before { + content: "\e0e6"; +} +.ph.ph-book-open-text:before { + content: "\e8f2"; +} +.ph.ph-book-open-user:before { + content: "\ede0"; +} +.ph.ph-bookmark:before { + content: "\e0e8"; +} +.ph.ph-bookmark-simple:before { + content: "\e0ea"; +} +.ph.ph-bookmarks:before { + content: "\e0ec"; +} +.ph.ph-bookmarks-simple:before { + content: "\e5f0"; +} +.ph.ph-books:before { + content: "\e758"; +} +.ph.ph-boot:before { + content: "\ecca"; +} +.ph.ph-boules:before { + content: "\e722"; +} +.ph.ph-bounding-box:before { + content: "\e6ce"; +} +.ph.ph-bowl-food:before { + content: "\eaa4"; +} +.ph.ph-bowl-steam:before { + content: "\e8e4"; +} +.ph.ph-bowling-ball:before { + content: "\ea34"; +} +.ph.ph-box-arrow-down:before { + content: "\e00e"; +} +.ph.ph-archive-box:before { + content: "\e00e"; +} +.ph.ph-box-arrow-up:before { + content: "\ee54"; +} +.ph.ph-boxing-glove:before { + content: "\ea36"; +} +.ph.ph-brackets-angle:before { + content: "\e862"; +} +.ph.ph-brackets-curly:before { + content: "\e860"; +} +.ph.ph-brackets-round:before { + content: "\e864"; +} +.ph.ph-brackets-square:before { + content: "\e85e"; +} +.ph.ph-brain:before { + content: "\e74e"; +} +.ph.ph-brandy:before { + content: "\e6b4"; +} +.ph.ph-bread:before { + content: "\e81c"; +} +.ph.ph-bridge:before { + content: "\ea68"; +} +.ph.ph-briefcase:before { + content: "\e0ee"; +} +.ph.ph-briefcase-metal:before { + content: "\e5f2"; +} +.ph.ph-broadcast:before { + content: "\e0f2"; +} +.ph.ph-broom:before { + content: "\ec54"; +} +.ph.ph-browser:before { + content: "\e0f4"; +} +.ph.ph-browsers:before { + content: "\e0f6"; +} +.ph.ph-bug:before { + content: "\e5f4"; +} +.ph.ph-bug-beetle:before { + content: "\e5f6"; +} +.ph.ph-bug-droid:before { + content: "\e5f8"; +} +.ph.ph-building:before { + content: "\e100"; +} +.ph.ph-building-apartment:before { + content: "\e0fe"; +} +.ph.ph-building-office:before { + content: "\e0ff"; +} +.ph.ph-buildings:before { + content: "\e102"; +} +.ph.ph-bulldozer:before { + content: "\ec6c"; +} +.ph.ph-bus:before { + content: "\e106"; +} +.ph.ph-butterfly:before { + content: "\ea6e"; +} +.ph.ph-cable-car:before { + content: "\e49c"; +} +.ph.ph-cactus:before { + content: "\e918"; +} +.ph.ph-cake:before { + content: "\e780"; +} +.ph.ph-calculator:before { + content: "\e538"; +} +.ph.ph-calendar:before { + content: "\e108"; +} +.ph.ph-calendar-blank:before { + content: "\e10a"; +} +.ph.ph-calendar-check:before { + content: "\e712"; +} +.ph.ph-calendar-dot:before { + content: "\e7b2"; +} +.ph.ph-calendar-dots:before { + content: "\e7b4"; +} +.ph.ph-calendar-heart:before { + content: "\e8b0"; +} +.ph.ph-calendar-minus:before { + content: "\ea14"; +} +.ph.ph-calendar-plus:before { + content: "\e714"; +} +.ph.ph-calendar-slash:before { + content: "\ea12"; +} +.ph.ph-calendar-star:before { + content: "\e8b2"; +} +.ph.ph-calendar-x:before { + content: "\e10c"; +} +.ph.ph-call-bell:before { + content: "\e7de"; +} +.ph.ph-camera:before { + content: "\e10e"; +} +.ph.ph-camera-plus:before { + content: "\ec58"; +} +.ph.ph-camera-rotate:before { + content: "\e7a4"; +} +.ph.ph-camera-slash:before { + content: "\e110"; +} +.ph.ph-campfire:before { + content: "\e9d8"; +} +.ph.ph-car:before { + content: "\e112"; +} +.ph.ph-car-battery:before { + content: "\ee30"; +} +.ph.ph-car-profile:before { + content: "\e8cc"; +} +.ph.ph-car-simple:before { + content: "\e114"; +} +.ph.ph-cardholder:before { + content: "\e5fa"; +} +.ph.ph-cards:before { + content: "\e0f8"; +} +.ph.ph-cards-three:before { + content: "\ee50"; +} +.ph.ph-caret-circle-double-down:before { + content: "\e116"; +} +.ph.ph-caret-circle-double-left:before { + content: "\e118"; +} +.ph.ph-caret-circle-double-right:before { + content: "\e11a"; +} +.ph.ph-caret-circle-double-up:before { + content: "\e11c"; +} +.ph.ph-caret-circle-down:before { + content: "\e11e"; +} +.ph.ph-caret-circle-left:before { + content: "\e120"; +} +.ph.ph-caret-circle-right:before { + content: "\e122"; +} +.ph.ph-caret-circle-up:before { + content: "\e124"; +} +.ph.ph-caret-circle-up-down:before { + content: "\e13e"; +} +.ph.ph-caret-double-down:before { + content: "\e126"; +} +.ph.ph-caret-double-left:before { + content: "\e128"; +} +.ph.ph-caret-double-right:before { + content: "\e12a"; +} +.ph.ph-caret-double-up:before { + content: "\e12c"; +} +.ph.ph-caret-down:before { + content: "\e136"; +} +.ph.ph-caret-left:before { + content: "\e138"; +} +.ph.ph-caret-line-down:before { + content: "\e134"; +} +.ph.ph-caret-line-left:before { + content: "\e132"; +} +.ph.ph-caret-line-right:before { + content: "\e130"; +} +.ph.ph-caret-line-up:before { + content: "\e12e"; +} +.ph.ph-caret-right:before { + content: "\e13a"; +} +.ph.ph-caret-up:before { + content: "\e13c"; +} +.ph.ph-caret-up-down:before { + content: "\e140"; +} +.ph.ph-carrot:before { + content: "\ed38"; +} +.ph.ph-cash-register:before { + content: "\ed80"; +} +.ph.ph-cassette-tape:before { + content: "\ed2e"; +} +.ph.ph-castle-turret:before { + content: "\e9d0"; +} +.ph.ph-cat:before { + content: "\e748"; +} +.ph.ph-cell-signal-full:before { + content: "\e142"; +} +.ph.ph-cell-signal-high:before { + content: "\e144"; +} +.ph.ph-cell-signal-low:before { + content: "\e146"; +} +.ph.ph-cell-signal-medium:before { + content: "\e148"; +} +.ph.ph-cell-signal-none:before { + content: "\e14a"; +} +.ph.ph-cell-signal-slash:before { + content: "\e14c"; +} +.ph.ph-cell-signal-x:before { + content: "\e14e"; +} +.ph.ph-cell-tower:before { + content: "\ebaa"; +} +.ph.ph-certificate:before { + content: "\e766"; +} +.ph.ph-chair:before { + content: "\e950"; +} +.ph.ph-chalkboard:before { + content: "\e5fc"; +} +.ph.ph-chalkboard-simple:before { + content: "\e5fe"; +} +.ph.ph-chalkboard-teacher:before { + content: "\e600"; +} +.ph.ph-champagne:before { + content: "\eaca"; +} +.ph.ph-charging-station:before { + content: "\e8d0"; +} +.ph.ph-chart-bar:before { + content: "\e150"; +} +.ph.ph-chart-bar-horizontal:before { + content: "\e152"; +} +.ph.ph-chart-donut:before { + content: "\eaa6"; +} +.ph.ph-chart-line:before { + content: "\e154"; +} +.ph.ph-chart-line-down:before { + content: "\e8b6"; +} +.ph.ph-chart-line-up:before { + content: "\e156"; +} +.ph.ph-chart-pie:before { + content: "\e158"; +} +.ph.ph-chart-pie-slice:before { + content: "\e15a"; +} +.ph.ph-chart-polar:before { + content: "\eaa8"; +} +.ph.ph-chart-scatter:before { + content: "\eaac"; +} +.ph.ph-chat:before { + content: "\e15c"; +} +.ph.ph-chat-centered:before { + content: "\e160"; +} +.ph.ph-chat-centered-dots:before { + content: "\e164"; +} +.ph.ph-chat-centered-slash:before { + content: "\e162"; +} +.ph.ph-chat-centered-text:before { + content: "\e166"; +} +.ph.ph-chat-circle:before { + content: "\e168"; +} +.ph.ph-chat-circle-dots:before { + content: "\e16c"; +} +.ph.ph-chat-circle-slash:before { + content: "\e16a"; +} +.ph.ph-chat-circle-text:before { + content: "\e16e"; +} +.ph.ph-chat-dots:before { + content: "\e170"; +} +.ph.ph-chat-slash:before { + content: "\e15e"; +} +.ph.ph-chat-teardrop:before { + content: "\e172"; +} +.ph.ph-chat-teardrop-dots:before { + content: "\e176"; +} +.ph.ph-chat-teardrop-slash:before { + content: "\e174"; +} +.ph.ph-chat-teardrop-text:before { + content: "\e178"; +} +.ph.ph-chat-text:before { + content: "\e17a"; +} +.ph.ph-chats:before { + content: "\e17c"; +} +.ph.ph-chats-circle:before { + content: "\e17e"; +} +.ph.ph-chats-teardrop:before { + content: "\e180"; +} +.ph.ph-check:before { + content: "\e182"; +} +.ph.ph-check-circle:before { + content: "\e184"; +} +.ph.ph-check-fat:before { + content: "\eba6"; +} +.ph.ph-check-square:before { + content: "\e186"; +} +.ph.ph-check-square-offset:before { + content: "\e188"; +} +.ph.ph-checkerboard:before { + content: "\e8c4"; +} +.ph.ph-checks:before { + content: "\e53a"; +} +.ph.ph-cheers:before { + content: "\ea4a"; +} +.ph.ph-cheese:before { + content: "\e9fe"; +} +.ph.ph-chef-hat:before { + content: "\ed8e"; +} +.ph.ph-cherries:before { + content: "\e830"; +} +.ph.ph-church:before { + content: "\ecea"; +} +.ph.ph-cigarette:before { + content: "\ed90"; +} +.ph.ph-cigarette-slash:before { + content: "\ed92"; +} +.ph.ph-circle:before { + content: "\e18a"; +} +.ph.ph-circle-dashed:before { + content: "\e602"; +} +.ph.ph-circle-half:before { + content: "\e18c"; +} +.ph.ph-circle-half-tilt:before { + content: "\e18e"; +} +.ph.ph-circle-notch:before { + content: "\eb44"; +} +.ph.ph-circles-four:before { + content: "\e190"; +} +.ph.ph-circles-three:before { + content: "\e192"; +} +.ph.ph-circles-three-plus:before { + content: "\e194"; +} +.ph.ph-circuitry:before { + content: "\e9c2"; +} +.ph.ph-city:before { + content: "\ea6a"; +} +.ph.ph-clipboard:before { + content: "\e196"; +} +.ph.ph-clipboard-text:before { + content: "\e198"; +} +.ph.ph-clock:before { + content: "\e19a"; +} +.ph.ph-clock-afternoon:before { + content: "\e19c"; +} +.ph.ph-clock-clockwise:before { + content: "\e19e"; +} +.ph.ph-clock-countdown:before { + content: "\ed2c"; +} +.ph.ph-clock-counter-clockwise:before { + content: "\e1a0"; +} +.ph.ph-clock-user:before { + content: "\edec"; +} +.ph.ph-closed-captioning:before { + content: "\e1a4"; +} +.ph.ph-cloud:before { + content: "\e1aa"; +} +.ph.ph-cloud-arrow-down:before { + content: "\e1ac"; +} +.ph.ph-cloud-arrow-up:before { + content: "\e1ae"; +} +.ph.ph-cloud-check:before { + content: "\e1b0"; +} +.ph.ph-cloud-fog:before { + content: "\e53c"; +} +.ph.ph-cloud-lightning:before { + content: "\e1b2"; +} +.ph.ph-cloud-moon:before { + content: "\e53e"; +} +.ph.ph-cloud-rain:before { + content: "\e1b4"; +} +.ph.ph-cloud-slash:before { + content: "\e1b6"; +} +.ph.ph-cloud-snow:before { + content: "\e1b8"; +} +.ph.ph-cloud-sun:before { + content: "\e540"; +} +.ph.ph-cloud-warning:before { + content: "\ea98"; +} +.ph.ph-cloud-x:before { + content: "\ea96"; +} +.ph.ph-clover:before { + content: "\edc8"; +} +.ph.ph-club:before { + content: "\e1ba"; +} +.ph.ph-coat-hanger:before { + content: "\e7fe"; +} +.ph.ph-coda-logo:before { + content: "\e7ce"; +} +.ph.ph-code:before { + content: "\e1bc"; +} +.ph.ph-code-block:before { + content: "\eafe"; +} +.ph.ph-code-simple:before { + content: "\e1be"; +} +.ph.ph-codepen-logo:before { + content: "\e978"; +} +.ph.ph-codesandbox-logo:before { + content: "\ea06"; +} +.ph.ph-coffee:before { + content: "\e1c2"; +} +.ph.ph-coffee-bean:before { + content: "\e1c0"; +} +.ph.ph-coin:before { + content: "\e60e"; +} +.ph.ph-coin-vertical:before { + content: "\eb48"; +} +.ph.ph-coins:before { + content: "\e78e"; +} +.ph.ph-columns:before { + content: "\e546"; +} +.ph.ph-columns-plus-left:before { + content: "\e544"; +} +.ph.ph-columns-plus-right:before { + content: "\e542"; +} +.ph.ph-command:before { + content: "\e1c4"; +} +.ph.ph-compass:before { + content: "\e1c8"; +} +.ph.ph-compass-rose:before { + content: "\e1c6"; +} +.ph.ph-compass-tool:before { + content: "\ea0e"; +} +.ph.ph-computer-tower:before { + content: "\e548"; +} +.ph.ph-confetti:before { + content: "\e81a"; +} +.ph.ph-contactless-payment:before { + content: "\ed42"; +} +.ph.ph-control:before { + content: "\eca6"; +} +.ph.ph-cookie:before { + content: "\e6ca"; +} +.ph.ph-cooking-pot:before { + content: "\e764"; +} +.ph.ph-copy:before { + content: "\e1ca"; +} +.ph.ph-copy-simple:before { + content: "\e1cc"; +} +.ph.ph-copyleft:before { + content: "\e86a"; +} +.ph.ph-copyright:before { + content: "\e54a"; +} +.ph.ph-corners-in:before { + content: "\e1ce"; +} +.ph.ph-corners-out:before { + content: "\e1d0"; +} +.ph.ph-couch:before { + content: "\e7f6"; +} +.ph.ph-court-basketball:before { + content: "\ee36"; +} +.ph.ph-cow:before { + content: "\eabe"; +} +.ph.ph-cowboy-hat:before { + content: "\ed12"; +} +.ph.ph-cpu:before { + content: "\e610"; +} +.ph.ph-crane:before { + content: "\ed48"; +} +.ph.ph-crane-tower:before { + content: "\ed49"; +} +.ph.ph-credit-card:before { + content: "\e1d2"; +} +.ph.ph-cricket:before { + content: "\ee12"; +} +.ph.ph-crop:before { + content: "\e1d4"; +} +.ph.ph-cross:before { + content: "\e8a0"; +} +.ph.ph-crosshair:before { + content: "\e1d6"; +} +.ph.ph-crosshair-simple:before { + content: "\e1d8"; +} +.ph.ph-crown:before { + content: "\e614"; +} +.ph.ph-crown-cross:before { + content: "\ee5e"; +} +.ph.ph-crown-simple:before { + content: "\e616"; +} +.ph.ph-cube:before { + content: "\e1da"; +} +.ph.ph-cube-focus:before { + content: "\ed0a"; +} +.ph.ph-cube-transparent:before { + content: "\ec7c"; +} +.ph.ph-currency-btc:before { + content: "\e618"; +} +.ph.ph-currency-circle-dollar:before { + content: "\e54c"; +} +.ph.ph-currency-cny:before { + content: "\e54e"; +} +.ph.ph-currency-dollar:before { + content: "\e550"; +} +.ph.ph-currency-dollar-simple:before { + content: "\e552"; +} +.ph.ph-currency-eth:before { + content: "\eada"; +} +.ph.ph-currency-eur:before { + content: "\e554"; +} +.ph.ph-currency-gbp:before { + content: "\e556"; +} +.ph.ph-currency-inr:before { + content: "\e558"; +} +.ph.ph-currency-jpy:before { + content: "\e55a"; +} +.ph.ph-currency-krw:before { + content: "\e55c"; +} +.ph.ph-currency-kzt:before { + content: "\ec4c"; +} +.ph.ph-currency-ngn:before { + content: "\eb52"; +} +.ph.ph-currency-rub:before { + content: "\e55e"; +} +.ph.ph-cursor:before { + content: "\e1dc"; +} +.ph.ph-cursor-click:before { + content: "\e7c8"; +} +.ph.ph-cursor-text:before { + content: "\e7d8"; +} +.ph.ph-cylinder:before { + content: "\e8fc"; +} +.ph.ph-database:before { + content: "\e1de"; +} +.ph.ph-desk:before { + content: "\ed16"; +} +.ph.ph-desktop:before { + content: "\e560"; +} +.ph.ph-desktop-tower:before { + content: "\e562"; +} +.ph.ph-detective:before { + content: "\e83e"; +} +.ph.ph-dev-to-logo:before { + content: "\ed0e"; +} +.ph.ph-device-mobile:before { + content: "\e1e0"; +} +.ph.ph-device-mobile-camera:before { + content: "\e1e2"; +} +.ph.ph-device-mobile-slash:before { + content: "\ee46"; +} +.ph.ph-device-mobile-speaker:before { + content: "\e1e4"; +} +.ph.ph-device-rotate:before { + content: "\edf2"; +} +.ph.ph-device-tablet:before { + content: "\e1e6"; +} +.ph.ph-device-tablet-camera:before { + content: "\e1e8"; +} +.ph.ph-device-tablet-speaker:before { + content: "\e1ea"; +} +.ph.ph-devices:before { + content: "\eba4"; +} +.ph.ph-diamond:before { + content: "\e1ec"; +} +.ph.ph-diamonds-four:before { + content: "\e8f4"; +} +.ph.ph-dice-five:before { + content: "\e1ee"; +} +.ph.ph-dice-four:before { + content: "\e1f0"; +} +.ph.ph-dice-one:before { + content: "\e1f2"; +} +.ph.ph-dice-six:before { + content: "\e1f4"; +} +.ph.ph-dice-three:before { + content: "\e1f6"; +} +.ph.ph-dice-two:before { + content: "\e1f8"; +} +.ph.ph-disc:before { + content: "\e564"; +} +.ph.ph-disco-ball:before { + content: "\ed98"; +} +.ph.ph-discord-logo:before { + content: "\e61a"; +} +.ph.ph-divide:before { + content: "\e1fa"; +} +.ph.ph-dna:before { + content: "\e924"; +} +.ph.ph-dog:before { + content: "\e74a"; +} +.ph.ph-door:before { + content: "\e61c"; +} +.ph.ph-door-open:before { + content: "\e7e6"; +} +.ph.ph-dot:before { + content: "\ecde"; +} +.ph.ph-dot-outline:before { + content: "\ece0"; +} +.ph.ph-dots-nine:before { + content: "\e1fc"; +} +.ph.ph-dots-six:before { + content: "\e794"; +} +.ph.ph-dots-six-vertical:before { + content: "\eae2"; +} +.ph.ph-dots-three:before { + content: "\e1fe"; +} +.ph.ph-dots-three-circle:before { + content: "\e200"; +} +.ph.ph-dots-three-circle-vertical:before { + content: "\e202"; +} +.ph.ph-dots-three-outline:before { + content: "\e204"; +} +.ph.ph-dots-three-outline-vertical:before { + content: "\e206"; +} +.ph.ph-dots-three-vertical:before { + content: "\e208"; +} +.ph.ph-download:before { + content: "\e20a"; +} +.ph.ph-download-simple:before { + content: "\e20c"; +} +.ph.ph-dress:before { + content: "\ea7e"; +} +.ph.ph-dresser:before { + content: "\e94e"; +} +.ph.ph-dribbble-logo:before { + content: "\e20e"; +} +.ph.ph-drone:before { + content: "\ed74"; +} +.ph.ph-drop:before { + content: "\e210"; +} +.ph.ph-drop-half:before { + content: "\e566"; +} +.ph.ph-drop-half-bottom:before { + content: "\eb40"; +} +.ph.ph-drop-simple:before { + content: "\ee32"; +} +.ph.ph-drop-slash:before { + content: "\e954"; +} +.ph.ph-dropbox-logo:before { + content: "\e7d0"; +} +.ph.ph-ear:before { + content: "\e70c"; +} +.ph.ph-ear-slash:before { + content: "\e70e"; +} +.ph.ph-egg:before { + content: "\e812"; +} +.ph.ph-egg-crack:before { + content: "\eb64"; +} +.ph.ph-eject:before { + content: "\e212"; +} +.ph.ph-eject-simple:before { + content: "\e6ae"; +} +.ph.ph-elevator:before { + content: "\ecc0"; +} +.ph.ph-empty:before { + content: "\edbc"; +} +.ph.ph-engine:before { + content: "\ea80"; +} +.ph.ph-envelope:before { + content: "\e214"; +} +.ph.ph-envelope-open:before { + content: "\e216"; +} +.ph.ph-envelope-simple:before { + content: "\e218"; +} +.ph.ph-envelope-simple-open:before { + content: "\e21a"; +} +.ph.ph-equalizer:before { + content: "\ebbc"; +} +.ph.ph-equals:before { + content: "\e21c"; +} +.ph.ph-eraser:before { + content: "\e21e"; +} +.ph.ph-escalator-down:before { + content: "\ecba"; +} +.ph.ph-escalator-up:before { + content: "\ecbc"; +} +.ph.ph-exam:before { + content: "\e742"; +} +.ph.ph-exclamation-mark:before { + content: "\ee44"; +} +.ph.ph-exclude:before { + content: "\e882"; +} +.ph.ph-exclude-square:before { + content: "\e880"; +} +.ph.ph-export:before { + content: "\eaf0"; +} +.ph.ph-eye:before { + content: "\e220"; +} +.ph.ph-eye-closed:before { + content: "\e222"; +} +.ph.ph-eye-slash:before { + content: "\e224"; +} +.ph.ph-eyedropper:before { + content: "\e568"; +} +.ph.ph-eyedropper-sample:before { + content: "\eac4"; +} +.ph.ph-eyeglasses:before { + content: "\e7ba"; +} +.ph.ph-eyes:before { + content: "\ee5c"; +} +.ph.ph-face-mask:before { + content: "\e56a"; +} +.ph.ph-facebook-logo:before { + content: "\e226"; +} +.ph.ph-factory:before { + content: "\e760"; +} +.ph.ph-faders:before { + content: "\e228"; +} +.ph.ph-faders-horizontal:before { + content: "\e22a"; +} +.ph.ph-fallout-shelter:before { + content: "\e9de"; +} +.ph.ph-fan:before { + content: "\e9f2"; +} +.ph.ph-farm:before { + content: "\ec70"; +} +.ph.ph-fast-forward:before { + content: "\e6a6"; +} +.ph.ph-fast-forward-circle:before { + content: "\e22c"; +} +.ph.ph-feather:before { + content: "\e9c0"; +} +.ph.ph-fediverse-logo:before { + content: "\ed66"; +} +.ph.ph-figma-logo:before { + content: "\e22e"; +} +.ph.ph-file:before { + content: "\e230"; +} +.ph.ph-file-archive:before { + content: "\eb2a"; +} +.ph.ph-file-arrow-down:before { + content: "\e232"; +} +.ph.ph-file-arrow-up:before { + content: "\e61e"; +} +.ph.ph-file-audio:before { + content: "\ea20"; +} +.ph.ph-file-c:before { + content: "\eb32"; +} +.ph.ph-file-c-sharp:before { + content: "\eb30"; +} +.ph.ph-file-cloud:before { + content: "\e95e"; +} +.ph.ph-file-code:before { + content: "\e914"; +} +.ph.ph-file-cpp:before { + content: "\eb2e"; +} +.ph.ph-file-css:before { + content: "\eb34"; +} +.ph.ph-file-csv:before { + content: "\eb1c"; +} +.ph.ph-file-dashed:before { + content: "\e704"; +} +.ph.ph-file-dotted:before { + content: "\e704"; +} +.ph.ph-file-doc:before { + content: "\eb1e"; +} +.ph.ph-file-html:before { + content: "\eb38"; +} +.ph.ph-file-image:before { + content: "\ea24"; +} +.ph.ph-file-ini:before { + content: "\eb33"; +} +.ph.ph-file-jpg:before { + content: "\eb1a"; +} +.ph.ph-file-js:before { + content: "\eb24"; +} +.ph.ph-file-jsx:before { + content: "\eb3a"; +} +.ph.ph-file-lock:before { + content: "\e95c"; +} +.ph.ph-file-magnifying-glass:before { + content: "\e238"; +} +.ph.ph-file-search:before { + content: "\e238"; +} +.ph.ph-file-md:before { + content: "\ed50"; +} +.ph.ph-file-minus:before { + content: "\e234"; +} +.ph.ph-file-pdf:before { + content: "\e702"; +} +.ph.ph-file-plus:before { + content: "\e236"; +} +.ph.ph-file-png:before { + content: "\eb18"; +} +.ph.ph-file-ppt:before { + content: "\eb20"; +} +.ph.ph-file-py:before { + content: "\eb2c"; +} +.ph.ph-file-rs:before { + content: "\eb28"; +} +.ph.ph-file-sql:before { + content: "\ed4e"; +} +.ph.ph-file-svg:before { + content: "\ed08"; +} +.ph.ph-file-text:before { + content: "\e23a"; +} +.ph.ph-file-ts:before { + content: "\eb26"; +} +.ph.ph-file-tsx:before { + content: "\eb3c"; +} +.ph.ph-file-txt:before { + content: "\eb35"; +} +.ph.ph-file-video:before { + content: "\ea22"; +} +.ph.ph-file-vue:before { + content: "\eb3e"; +} +.ph.ph-file-x:before { + content: "\e23c"; +} +.ph.ph-file-xls:before { + content: "\eb22"; +} +.ph.ph-file-zip:before { + content: "\e958"; +} +.ph.ph-files:before { + content: "\e710"; +} +.ph.ph-film-reel:before { + content: "\e8c0"; +} +.ph.ph-film-script:before { + content: "\eb50"; +} +.ph.ph-film-slate:before { + content: "\e8c2"; +} +.ph.ph-film-strip:before { + content: "\e792"; +} +.ph.ph-fingerprint:before { + content: "\e23e"; +} +.ph.ph-fingerprint-simple:before { + content: "\e240"; +} +.ph.ph-finn-the-human:before { + content: "\e56c"; +} +.ph.ph-fire:before { + content: "\e242"; +} +.ph.ph-fire-extinguisher:before { + content: "\e9e8"; +} +.ph.ph-fire-simple:before { + content: "\e620"; +} +.ph.ph-fire-truck:before { + content: "\e574"; +} +.ph.ph-first-aid:before { + content: "\e56e"; +} +.ph.ph-first-aid-kit:before { + content: "\e570"; +} +.ph.ph-fish:before { + content: "\e728"; +} +.ph.ph-fish-simple:before { + content: "\e72a"; +} +.ph.ph-flag:before { + content: "\e244"; +} +.ph.ph-flag-banner:before { + content: "\e622"; +} +.ph.ph-flag-banner-fold:before { + content: "\ecf2"; +} +.ph.ph-flag-checkered:before { + content: "\ea38"; +} +.ph.ph-flag-pennant:before { + content: "\ecf0"; +} +.ph.ph-flame:before { + content: "\e624"; +} +.ph.ph-flashlight:before { + content: "\e246"; +} +.ph.ph-flask:before { + content: "\e79e"; +} +.ph.ph-flip-horizontal:before { + content: "\ed6a"; +} +.ph.ph-flip-vertical:before { + content: "\ed6c"; +} +.ph.ph-floppy-disk:before { + content: "\e248"; +} +.ph.ph-floppy-disk-back:before { + content: "\eaf4"; +} +.ph.ph-flow-arrow:before { + content: "\e6ec"; +} +.ph.ph-flower:before { + content: "\e75e"; +} +.ph.ph-flower-lotus:before { + content: "\e6cc"; +} +.ph.ph-flower-tulip:before { + content: "\eacc"; +} +.ph.ph-flying-saucer:before { + content: "\eb4a"; +} +.ph.ph-folder:before { + content: "\e24a"; +} +.ph.ph-folder-notch:before { + content: "\e24a"; +} +.ph.ph-folder-dashed:before { + content: "\e8f8"; +} +.ph.ph-folder-dotted:before { + content: "\e8f8"; +} +.ph.ph-folder-lock:before { + content: "\ea3c"; +} +.ph.ph-folder-minus:before { + content: "\e254"; +} +.ph.ph-folder-notch-minus:before { + content: "\e254"; +} +.ph.ph-folder-open:before { + content: "\e256"; +} +.ph.ph-folder-notch-open:before { + content: "\e256"; +} +.ph.ph-folder-plus:before { + content: "\e258"; +} +.ph.ph-folder-notch-plus:before { + content: "\e258"; +} +.ph.ph-folder-simple:before { + content: "\e25a"; +} +.ph.ph-folder-simple-dashed:before { + content: "\ec2a"; +} +.ph.ph-folder-simple-dotted:before { + content: "\ec2a"; +} +.ph.ph-folder-simple-lock:before { + content: "\eb5e"; +} +.ph.ph-folder-simple-minus:before { + content: "\e25c"; +} +.ph.ph-folder-simple-plus:before { + content: "\e25e"; +} +.ph.ph-folder-simple-star:before { + content: "\ec2e"; +} +.ph.ph-folder-simple-user:before { + content: "\eb60"; +} +.ph.ph-folder-star:before { + content: "\ea86"; +} +.ph.ph-folder-user:before { + content: "\eb46"; +} +.ph.ph-folders:before { + content: "\e260"; +} +.ph.ph-football:before { + content: "\e718"; +} +.ph.ph-football-helmet:before { + content: "\ee4c"; +} +.ph.ph-footprints:before { + content: "\ea88"; +} +.ph.ph-fork-knife:before { + content: "\e262"; +} +.ph.ph-four-k:before { + content: "\ea5c"; +} +.ph.ph-frame-corners:before { + content: "\e626"; +} +.ph.ph-framer-logo:before { + content: "\e264"; +} +.ph.ph-function:before { + content: "\ebe4"; +} +.ph.ph-funnel:before { + content: "\e266"; +} +.ph.ph-funnel-simple:before { + content: "\e268"; +} +.ph.ph-funnel-simple-x:before { + content: "\e26a"; +} +.ph.ph-funnel-x:before { + content: "\e26c"; +} +.ph.ph-game-controller:before { + content: "\e26e"; +} +.ph.ph-garage:before { + content: "\ecd6"; +} +.ph.ph-gas-can:before { + content: "\e8ce"; +} +.ph.ph-gas-pump:before { + content: "\e768"; +} +.ph.ph-gauge:before { + content: "\e628"; +} +.ph.ph-gavel:before { + content: "\ea32"; +} +.ph.ph-gear:before { + content: "\e270"; +} +.ph.ph-gear-fine:before { + content: "\e87c"; +} +.ph.ph-gear-six:before { + content: "\e272"; +} +.ph.ph-gender-female:before { + content: "\e6e0"; +} +.ph.ph-gender-intersex:before { + content: "\e6e6"; +} +.ph.ph-gender-male:before { + content: "\e6e2"; +} +.ph.ph-gender-neuter:before { + content: "\e6ea"; +} +.ph.ph-gender-nonbinary:before { + content: "\e6e4"; +} +.ph.ph-gender-transgender:before { + content: "\e6e8"; +} +.ph.ph-ghost:before { + content: "\e62a"; +} +.ph.ph-gif:before { + content: "\e274"; +} +.ph.ph-gift:before { + content: "\e276"; +} +.ph.ph-git-branch:before { + content: "\e278"; +} +.ph.ph-git-commit:before { + content: "\e27a"; +} +.ph.ph-git-diff:before { + content: "\e27c"; +} +.ph.ph-git-fork:before { + content: "\e27e"; +} +.ph.ph-git-merge:before { + content: "\e280"; +} +.ph.ph-git-pull-request:before { + content: "\e282"; +} +.ph.ph-github-logo:before { + content: "\e576"; +} +.ph.ph-gitlab-logo:before { + content: "\e694"; +} +.ph.ph-gitlab-logo-simple:before { + content: "\e696"; +} +.ph.ph-globe:before { + content: "\e288"; +} +.ph.ph-globe-hemisphere-east:before { + content: "\e28a"; +} +.ph.ph-globe-hemisphere-west:before { + content: "\e28c"; +} +.ph.ph-globe-simple:before { + content: "\e28e"; +} +.ph.ph-globe-simple-x:before { + content: "\e284"; +} +.ph.ph-globe-stand:before { + content: "\e290"; +} +.ph.ph-globe-x:before { + content: "\e286"; +} +.ph.ph-goggles:before { + content: "\ecb4"; +} +.ph.ph-golf:before { + content: "\ea3e"; +} +.ph.ph-goodreads-logo:before { + content: "\ed10"; +} +.ph.ph-google-cardboard-logo:before { + content: "\e7b6"; +} +.ph.ph-google-chrome-logo:before { + content: "\e976"; +} +.ph.ph-google-drive-logo:before { + content: "\e8f6"; +} +.ph.ph-google-logo:before { + content: "\e292"; +} +.ph.ph-google-photos-logo:before { + content: "\eb92"; +} +.ph.ph-google-play-logo:before { + content: "\e294"; +} +.ph.ph-google-podcasts-logo:before { + content: "\eb94"; +} +.ph.ph-gps:before { + content: "\edd8"; +} +.ph.ph-gps-fix:before { + content: "\edd6"; +} +.ph.ph-gps-slash:before { + content: "\edd4"; +} +.ph.ph-gradient:before { + content: "\eb42"; +} +.ph.ph-graduation-cap:before { + content: "\e62c"; +} +.ph.ph-grains:before { + content: "\ec68"; +} +.ph.ph-grains-slash:before { + content: "\ec6a"; +} +.ph.ph-graph:before { + content: "\eb58"; +} +.ph.ph-graphics-card:before { + content: "\e612"; +} +.ph.ph-greater-than:before { + content: "\edc4"; +} +.ph.ph-greater-than-or-equal:before { + content: "\eda2"; +} +.ph.ph-grid-four:before { + content: "\e296"; +} +.ph.ph-grid-nine:before { + content: "\ec8c"; +} +.ph.ph-guitar:before { + content: "\ea8a"; +} +.ph.ph-hair-dryer:before { + content: "\ea66"; +} +.ph.ph-hamburger:before { + content: "\e790"; +} +.ph.ph-hammer:before { + content: "\e80e"; +} +.ph.ph-hand:before { + content: "\e298"; +} +.ph.ph-hand-arrow-down:before { + content: "\ea4e"; +} +.ph.ph-hand-arrow-up:before { + content: "\ee5a"; +} +.ph.ph-hand-coins:before { + content: "\ea8c"; +} +.ph.ph-hand-deposit:before { + content: "\ee82"; +} +.ph.ph-hand-eye:before { + content: "\ea4c"; +} +.ph.ph-hand-fist:before { + content: "\e57a"; +} +.ph.ph-hand-grabbing:before { + content: "\e57c"; +} +.ph.ph-hand-heart:before { + content: "\e810"; +} +.ph.ph-hand-palm:before { + content: "\e57e"; +} +.ph.ph-hand-peace:before { + content: "\e7cc"; +} +.ph.ph-hand-pointing:before { + content: "\e29a"; +} +.ph.ph-hand-soap:before { + content: "\e630"; +} +.ph.ph-hand-swipe-left:before { + content: "\ec94"; +} +.ph.ph-hand-swipe-right:before { + content: "\ec92"; +} +.ph.ph-hand-tap:before { + content: "\ec90"; +} +.ph.ph-hand-waving:before { + content: "\e580"; +} +.ph.ph-hand-withdraw:before { + content: "\ee80"; +} +.ph.ph-handbag:before { + content: "\e29c"; +} +.ph.ph-handbag-simple:before { + content: "\e62e"; +} +.ph.ph-hands-clapping:before { + content: "\e6a0"; +} +.ph.ph-hands-praying:before { + content: "\ecc8"; +} +.ph.ph-handshake:before { + content: "\e582"; +} +.ph.ph-hard-drive:before { + content: "\e29e"; +} +.ph.ph-hard-drives:before { + content: "\e2a0"; +} +.ph.ph-hard-hat:before { + content: "\ed46"; +} +.ph.ph-hash:before { + content: "\e2a2"; +} +.ph.ph-hash-straight:before { + content: "\e2a4"; +} +.ph.ph-head-circuit:before { + content: "\e7d4"; +} +.ph.ph-headlights:before { + content: "\e6fe"; +} +.ph.ph-headphones:before { + content: "\e2a6"; +} +.ph.ph-headset:before { + content: "\e584"; +} +.ph.ph-heart:before { + content: "\e2a8"; +} +.ph.ph-heart-break:before { + content: "\ebe8"; +} +.ph.ph-heart-half:before { + content: "\ec48"; +} +.ph.ph-heart-straight:before { + content: "\e2aa"; +} +.ph.ph-heart-straight-break:before { + content: "\eb98"; +} +.ph.ph-heartbeat:before { + content: "\e2ac"; +} +.ph.ph-hexagon:before { + content: "\e2ae"; +} +.ph.ph-high-definition:before { + content: "\ea8e"; +} +.ph.ph-high-heel:before { + content: "\e8e8"; +} +.ph.ph-highlighter:before { + content: "\ec76"; +} +.ph.ph-highlighter-circle:before { + content: "\e632"; +} +.ph.ph-hockey:before { + content: "\ec86"; +} +.ph.ph-hoodie:before { + content: "\ecd0"; +} +.ph.ph-horse:before { + content: "\e2b0"; +} +.ph.ph-hospital:before { + content: "\e844"; +} +.ph.ph-hourglass:before { + content: "\e2b2"; +} +.ph.ph-hourglass-high:before { + content: "\e2b4"; +} +.ph.ph-hourglass-low:before { + content: "\e2b6"; +} +.ph.ph-hourglass-medium:before { + content: "\e2b8"; +} +.ph.ph-hourglass-simple:before { + content: "\e2ba"; +} +.ph.ph-hourglass-simple-high:before { + content: "\e2bc"; +} +.ph.ph-hourglass-simple-low:before { + content: "\e2be"; +} +.ph.ph-hourglass-simple-medium:before { + content: "\e2c0"; +} +.ph.ph-house:before { + content: "\e2c2"; +} +.ph.ph-house-line:before { + content: "\e2c4"; +} +.ph.ph-house-simple:before { + content: "\e2c6"; +} +.ph.ph-hurricane:before { + content: "\e88e"; +} +.ph.ph-ice-cream:before { + content: "\e804"; +} +.ph.ph-identification-badge:before { + content: "\e6f6"; +} +.ph.ph-identification-card:before { + content: "\e2c8"; +} +.ph.ph-image:before { + content: "\e2ca"; +} +.ph.ph-image-broken:before { + content: "\e7a8"; +} +.ph.ph-image-square:before { + content: "\e2cc"; +} +.ph.ph-images:before { + content: "\e836"; +} +.ph.ph-images-square:before { + content: "\e834"; +} +.ph.ph-infinity:before { + content: "\e634"; +} +.ph.ph-lemniscate:before { + content: "\e634"; +} +.ph.ph-info:before { + content: "\e2ce"; +} +.ph.ph-instagram-logo:before { + content: "\e2d0"; +} +.ph.ph-intersect:before { + content: "\e2d2"; +} +.ph.ph-intersect-square:before { + content: "\e87a"; +} +.ph.ph-intersect-three:before { + content: "\ecc4"; +} +.ph.ph-intersection:before { + content: "\edba"; +} +.ph.ph-invoice:before { + content: "\ee42"; +} +.ph.ph-island:before { + content: "\ee06"; +} +.ph.ph-jar:before { + content: "\e7e0"; +} +.ph.ph-jar-label:before { + content: "\e7e1"; +} +.ph.ph-jeep:before { + content: "\e2d4"; +} +.ph.ph-joystick:before { + content: "\ea5e"; +} +.ph.ph-kanban:before { + content: "\eb54"; +} +.ph.ph-key:before { + content: "\e2d6"; +} +.ph.ph-key-return:before { + content: "\e782"; +} +.ph.ph-keyboard:before { + content: "\e2d8"; +} +.ph.ph-keyhole:before { + content: "\ea78"; +} +.ph.ph-knife:before { + content: "\e636"; +} +.ph.ph-ladder:before { + content: "\e9e4"; +} +.ph.ph-ladder-simple:before { + content: "\ec26"; +} +.ph.ph-lamp:before { + content: "\e638"; +} +.ph.ph-lamp-pendant:before { + content: "\ee2e"; +} +.ph.ph-laptop:before { + content: "\e586"; +} +.ph.ph-lasso:before { + content: "\edc6"; +} +.ph.ph-lastfm-logo:before { + content: "\e842"; +} +.ph.ph-layout:before { + content: "\e6d6"; +} +.ph.ph-leaf:before { + content: "\e2da"; +} +.ph.ph-lectern:before { + content: "\e95a"; +} +.ph.ph-lego:before { + content: "\e8c6"; +} +.ph.ph-lego-smiley:before { + content: "\e8c7"; +} +.ph.ph-less-than:before { + content: "\edac"; +} +.ph.ph-less-than-or-equal:before { + content: "\eda4"; +} +.ph.ph-letter-circle-h:before { + content: "\ebf8"; +} +.ph.ph-letter-circle-p:before { + content: "\ec08"; +} +.ph.ph-letter-circle-v:before { + content: "\ec14"; +} +.ph.ph-lifebuoy:before { + content: "\e63a"; +} +.ph.ph-lightbulb:before { + content: "\e2dc"; +} +.ph.ph-lightbulb-filament:before { + content: "\e63c"; +} +.ph.ph-lighthouse:before { + content: "\e9f6"; +} +.ph.ph-lightning:before { + content: "\e2de"; +} +.ph.ph-lightning-a:before { + content: "\ea84"; +} +.ph.ph-lightning-slash:before { + content: "\e2e0"; +} +.ph.ph-line-segment:before { + content: "\e6d2"; +} +.ph.ph-line-segments:before { + content: "\e6d4"; +} +.ph.ph-line-vertical:before { + content: "\ed70"; +} +.ph.ph-link:before { + content: "\e2e2"; +} +.ph.ph-link-break:before { + content: "\e2e4"; +} +.ph.ph-link-simple:before { + content: "\e2e6"; +} +.ph.ph-link-simple-break:before { + content: "\e2e8"; +} +.ph.ph-link-simple-horizontal:before { + content: "\e2ea"; +} +.ph.ph-link-simple-horizontal-break:before { + content: "\e2ec"; +} +.ph.ph-linkedin-logo:before { + content: "\e2ee"; +} +.ph.ph-linktree-logo:before { + content: "\edee"; +} +.ph.ph-linux-logo:before { + content: "\eb02"; +} +.ph.ph-list:before { + content: "\e2f0"; +} +.ph.ph-list-bullets:before { + content: "\e2f2"; +} +.ph.ph-list-checks:before { + content: "\eadc"; +} +.ph.ph-list-dashes:before { + content: "\e2f4"; +} +.ph.ph-list-heart:before { + content: "\ebde"; +} +.ph.ph-list-magnifying-glass:before { + content: "\ebe0"; +} +.ph.ph-list-numbers:before { + content: "\e2f6"; +} +.ph.ph-list-plus:before { + content: "\e2f8"; +} +.ph.ph-list-star:before { + content: "\ebdc"; +} +.ph.ph-lock:before { + content: "\e2fa"; +} +.ph.ph-lock-key:before { + content: "\e2fe"; +} +.ph.ph-lock-key-open:before { + content: "\e300"; +} +.ph.ph-lock-laminated:before { + content: "\e302"; +} +.ph.ph-lock-laminated-open:before { + content: "\e304"; +} +.ph.ph-lock-open:before { + content: "\e306"; +} +.ph.ph-lock-simple:before { + content: "\e308"; +} +.ph.ph-lock-simple-open:before { + content: "\e30a"; +} +.ph.ph-lockers:before { + content: "\ecb8"; +} +.ph.ph-log:before { + content: "\ed82"; +} +.ph.ph-magic-wand:before { + content: "\e6b6"; +} +.ph.ph-magnet:before { + content: "\e680"; +} +.ph.ph-magnet-straight:before { + content: "\e682"; +} +.ph.ph-magnifying-glass:before { + content: "\e30c"; +} +.ph.ph-magnifying-glass-minus:before { + content: "\e30e"; +} +.ph.ph-magnifying-glass-plus:before { + content: "\e310"; +} +.ph.ph-mailbox:before { + content: "\ec1e"; +} +.ph.ph-map-pin:before { + content: "\e316"; +} +.ph.ph-map-pin-area:before { + content: "\ee3a"; +} +.ph.ph-map-pin-line:before { + content: "\e318"; +} +.ph.ph-map-pin-plus:before { + content: "\e314"; +} +.ph.ph-map-pin-simple:before { + content: "\ee3e"; +} +.ph.ph-map-pin-simple-area:before { + content: "\ee3c"; +} +.ph.ph-map-pin-simple-line:before { + content: "\ee38"; +} +.ph.ph-map-trifold:before { + content: "\e31a"; +} +.ph.ph-markdown-logo:before { + content: "\e508"; +} +.ph.ph-marker-circle:before { + content: "\e640"; +} +.ph.ph-martini:before { + content: "\e31c"; +} +.ph.ph-mask-happy:before { + content: "\e9f4"; +} +.ph.ph-mask-sad:before { + content: "\eb9e"; +} +.ph.ph-mastodon-logo:before { + content: "\ed68"; +} +.ph.ph-math-operations:before { + content: "\e31e"; +} +.ph.ph-matrix-logo:before { + content: "\ed64"; +} +.ph.ph-medal:before { + content: "\e320"; +} +.ph.ph-medal-military:before { + content: "\ecfc"; +} +.ph.ph-medium-logo:before { + content: "\e322"; +} +.ph.ph-megaphone:before { + content: "\e324"; +} +.ph.ph-megaphone-simple:before { + content: "\e642"; +} +.ph.ph-member-of:before { + content: "\edc2"; +} +.ph.ph-memory:before { + content: "\e9c4"; +} +.ph.ph-messenger-logo:before { + content: "\e6d8"; +} +.ph.ph-meta-logo:before { + content: "\ed02"; +} +.ph.ph-meteor:before { + content: "\e9ba"; +} +.ph.ph-metronome:before { + content: "\ec8e"; +} +.ph.ph-microphone:before { + content: "\e326"; +} +.ph.ph-microphone-slash:before { + content: "\e328"; +} +.ph.ph-microphone-stage:before { + content: "\e75c"; +} +.ph.ph-microscope:before { + content: "\ec7a"; +} +.ph.ph-microsoft-excel-logo:before { + content: "\eb6c"; +} +.ph.ph-microsoft-outlook-logo:before { + content: "\eb70"; +} +.ph.ph-microsoft-powerpoint-logo:before { + content: "\eace"; +} +.ph.ph-microsoft-teams-logo:before { + content: "\eb66"; +} +.ph.ph-microsoft-word-logo:before { + content: "\eb6a"; +} +.ph.ph-minus:before { + content: "\e32a"; +} +.ph.ph-minus-circle:before { + content: "\e32c"; +} +.ph.ph-minus-square:before { + content: "\ed4c"; +} +.ph.ph-money:before { + content: "\e588"; +} +.ph.ph-money-wavy:before { + content: "\ee68"; +} +.ph.ph-monitor:before { + content: "\e32e"; +} +.ph.ph-monitor-arrow-up:before { + content: "\e58a"; +} +.ph.ph-monitor-play:before { + content: "\e58c"; +} +.ph.ph-moon:before { + content: "\e330"; +} +.ph.ph-moon-stars:before { + content: "\e58e"; +} +.ph.ph-moped:before { + content: "\e824"; +} +.ph.ph-moped-front:before { + content: "\e822"; +} +.ph.ph-mosque:before { + content: "\ecee"; +} +.ph.ph-motorcycle:before { + content: "\e80a"; +} +.ph.ph-mountains:before { + content: "\e7ae"; +} +.ph.ph-mouse:before { + content: "\e33a"; +} +.ph.ph-mouse-left-click:before { + content: "\e334"; +} +.ph.ph-mouse-middle-click:before { + content: "\e338"; +} +.ph.ph-mouse-right-click:before { + content: "\e336"; +} +.ph.ph-mouse-scroll:before { + content: "\e332"; +} +.ph.ph-mouse-simple:before { + content: "\e644"; +} +.ph.ph-music-note:before { + content: "\e33c"; +} +.ph.ph-music-note-simple:before { + content: "\e33e"; +} +.ph.ph-music-notes:before { + content: "\e340"; +} +.ph.ph-music-notes-minus:before { + content: "\ee0c"; +} +.ph.ph-music-notes-plus:before { + content: "\eb7c"; +} +.ph.ph-music-notes-simple:before { + content: "\e342"; +} +.ph.ph-navigation-arrow:before { + content: "\eade"; +} +.ph.ph-needle:before { + content: "\e82e"; +} +.ph.ph-network:before { + content: "\edde"; +} +.ph.ph-network-slash:before { + content: "\eddc"; +} +.ph.ph-network-x:before { + content: "\edda"; +} +.ph.ph-newspaper:before { + content: "\e344"; +} +.ph.ph-newspaper-clipping:before { + content: "\e346"; +} +.ph.ph-not-equals:before { + content: "\eda6"; +} +.ph.ph-not-member-of:before { + content: "\edae"; +} +.ph.ph-not-subset-of:before { + content: "\edb0"; +} +.ph.ph-not-superset-of:before { + content: "\edb2"; +} +.ph.ph-notches:before { + content: "\ed3a"; +} +.ph.ph-note:before { + content: "\e348"; +} +.ph.ph-note-blank:before { + content: "\e34a"; +} +.ph.ph-note-pencil:before { + content: "\e34c"; +} +.ph.ph-notebook:before { + content: "\e34e"; +} +.ph.ph-notepad:before { + content: "\e63e"; +} +.ph.ph-notification:before { + content: "\e6fa"; +} +.ph.ph-notion-logo:before { + content: "\e9a0"; +} +.ph.ph-nuclear-plant:before { + content: "\ed7c"; +} +.ph.ph-number-circle-eight:before { + content: "\e352"; +} +.ph.ph-number-circle-five:before { + content: "\e358"; +} +.ph.ph-number-circle-four:before { + content: "\e35e"; +} +.ph.ph-number-circle-nine:before { + content: "\e364"; +} +.ph.ph-number-circle-one:before { + content: "\e36a"; +} +.ph.ph-number-circle-seven:before { + content: "\e370"; +} +.ph.ph-number-circle-six:before { + content: "\e376"; +} +.ph.ph-number-circle-three:before { + content: "\e37c"; +} +.ph.ph-number-circle-two:before { + content: "\e382"; +} +.ph.ph-number-circle-zero:before { + content: "\e388"; +} +.ph.ph-number-eight:before { + content: "\e350"; +} +.ph.ph-number-five:before { + content: "\e356"; +} +.ph.ph-number-four:before { + content: "\e35c"; +} +.ph.ph-number-nine:before { + content: "\e362"; +} +.ph.ph-number-one:before { + content: "\e368"; +} +.ph.ph-number-seven:before { + content: "\e36e"; +} +.ph.ph-number-six:before { + content: "\e374"; +} +.ph.ph-number-square-eight:before { + content: "\e354"; +} +.ph.ph-number-square-five:before { + content: "\e35a"; +} +.ph.ph-number-square-four:before { + content: "\e360"; +} +.ph.ph-number-square-nine:before { + content: "\e366"; +} +.ph.ph-number-square-one:before { + content: "\e36c"; +} +.ph.ph-number-square-seven:before { + content: "\e372"; +} +.ph.ph-number-square-six:before { + content: "\e378"; +} +.ph.ph-number-square-three:before { + content: "\e37e"; +} +.ph.ph-number-square-two:before { + content: "\e384"; +} +.ph.ph-number-square-zero:before { + content: "\e38a"; +} +.ph.ph-number-three:before { + content: "\e37a"; +} +.ph.ph-number-two:before { + content: "\e380"; +} +.ph.ph-number-zero:before { + content: "\e386"; +} +.ph.ph-numpad:before { + content: "\e3c8"; +} +.ph.ph-nut:before { + content: "\e38c"; +} +.ph.ph-ny-times-logo:before { + content: "\e646"; +} +.ph.ph-octagon:before { + content: "\e38e"; +} +.ph.ph-office-chair:before { + content: "\ea46"; +} +.ph.ph-onigiri:before { + content: "\ee2c"; +} +.ph.ph-open-ai-logo:before { + content: "\e7d2"; +} +.ph.ph-option:before { + content: "\e8a8"; +} +.ph.ph-orange:before { + content: "\ee40"; +} +.ph.ph-orange-slice:before { + content: "\ed36"; +} +.ph.ph-oven:before { + content: "\ed8c"; +} +.ph.ph-package:before { + content: "\e390"; +} +.ph.ph-paint-brush:before { + content: "\e6f0"; +} +.ph.ph-paint-brush-broad:before { + content: "\e590"; +} +.ph.ph-paint-brush-household:before { + content: "\e6f2"; +} +.ph.ph-paint-bucket:before { + content: "\e392"; +} +.ph.ph-paint-roller:before { + content: "\e6f4"; +} +.ph.ph-palette:before { + content: "\e6c8"; +} +.ph.ph-panorama:before { + content: "\eaa2"; +} +.ph.ph-pants:before { + content: "\ec88"; +} +.ph.ph-paper-plane:before { + content: "\e394"; +} +.ph.ph-paper-plane-right:before { + content: "\e396"; +} +.ph.ph-paper-plane-tilt:before { + content: "\e398"; +} +.ph.ph-paperclip:before { + content: "\e39a"; +} +.ph.ph-paperclip-horizontal:before { + content: "\e592"; +} +.ph.ph-parachute:before { + content: "\ea7c"; +} +.ph.ph-paragraph:before { + content: "\e960"; +} +.ph.ph-parallelogram:before { + content: "\ecc6"; +} +.ph.ph-park:before { + content: "\ecb2"; +} +.ph.ph-password:before { + content: "\e752"; +} +.ph.ph-path:before { + content: "\e39c"; +} +.ph.ph-patreon-logo:before { + content: "\e98a"; +} +.ph.ph-pause:before { + content: "\e39e"; +} +.ph.ph-pause-circle:before { + content: "\e3a0"; +} +.ph.ph-paw-print:before { + content: "\e648"; +} +.ph.ph-paypal-logo:before { + content: "\e98c"; +} +.ph.ph-peace:before { + content: "\e3a2"; +} +.ph.ph-pen:before { + content: "\e3aa"; +} +.ph.ph-pen-nib:before { + content: "\e3ac"; +} +.ph.ph-pen-nib-straight:before { + content: "\e64a"; +} +.ph.ph-pencil:before { + content: "\e3ae"; +} +.ph.ph-pencil-circle:before { + content: "\e3b0"; +} +.ph.ph-pencil-line:before { + content: "\e3b2"; +} +.ph.ph-pencil-ruler:before { + content: "\e906"; +} +.ph.ph-pencil-simple:before { + content: "\e3b4"; +} +.ph.ph-pencil-simple-line:before { + content: "\ebc6"; +} +.ph.ph-pencil-simple-slash:before { + content: "\ecf6"; +} +.ph.ph-pencil-slash:before { + content: "\ecf8"; +} +.ph.ph-pentagon:before { + content: "\ec7e"; +} +.ph.ph-pentagram:before { + content: "\ec5c"; +} +.ph.ph-pepper:before { + content: "\e94a"; +} +.ph.ph-percent:before { + content: "\e3b6"; +} +.ph.ph-person:before { + content: "\e3a8"; +} +.ph.ph-person-arms-spread:before { + content: "\ecfe"; +} +.ph.ph-person-simple:before { + content: "\e72e"; +} +.ph.ph-person-simple-bike:before { + content: "\e734"; +} +.ph.ph-person-simple-circle:before { + content: "\ee58"; +} +.ph.ph-person-simple-hike:before { + content: "\ed54"; +} +.ph.ph-person-simple-run:before { + content: "\e730"; +} +.ph.ph-person-simple-ski:before { + content: "\e71c"; +} +.ph.ph-person-simple-snowboard:before { + content: "\e71e"; +} +.ph.ph-person-simple-swim:before { + content: "\e736"; +} +.ph.ph-person-simple-tai-chi:before { + content: "\ed5c"; +} +.ph.ph-person-simple-throw:before { + content: "\e732"; +} +.ph.ph-person-simple-walk:before { + content: "\e73a"; +} +.ph.ph-perspective:before { + content: "\ebe6"; +} +.ph.ph-phone:before { + content: "\e3b8"; +} +.ph.ph-phone-call:before { + content: "\e3ba"; +} +.ph.ph-phone-disconnect:before { + content: "\e3bc"; +} +.ph.ph-phone-incoming:before { + content: "\e3be"; +} +.ph.ph-phone-list:before { + content: "\e3cc"; +} +.ph.ph-phone-outgoing:before { + content: "\e3c0"; +} +.ph.ph-phone-pause:before { + content: "\e3ca"; +} +.ph.ph-phone-plus:before { + content: "\ec56"; +} +.ph.ph-phone-slash:before { + content: "\e3c2"; +} +.ph.ph-phone-transfer:before { + content: "\e3c6"; +} +.ph.ph-phone-x:before { + content: "\e3c4"; +} +.ph.ph-phosphor-logo:before { + content: "\e3ce"; +} +.ph.ph-pi:before { + content: "\ec80"; +} +.ph.ph-piano-keys:before { + content: "\e9c8"; +} +.ph.ph-picnic-table:before { + content: "\ee26"; +} +.ph.ph-picture-in-picture:before { + content: "\e64c"; +} +.ph.ph-piggy-bank:before { + content: "\ea04"; +} +.ph.ph-pill:before { + content: "\e700"; +} +.ph.ph-ping-pong:before { + content: "\ea42"; +} +.ph.ph-pint-glass:before { + content: "\edd0"; +} +.ph.ph-pinterest-logo:before { + content: "\e64e"; +} +.ph.ph-pinwheel:before { + content: "\eb9c"; +} +.ph.ph-pipe:before { + content: "\ed86"; +} +.ph.ph-pipe-wrench:before { + content: "\ed88"; +} +.ph.ph-pix-logo:before { + content: "\ecc2"; +} +.ph.ph-pizza:before { + content: "\e796"; +} +.ph.ph-placeholder:before { + content: "\e650"; +} +.ph.ph-planet:before { + content: "\e652"; +} +.ph.ph-plant:before { + content: "\ebae"; +} +.ph.ph-play:before { + content: "\e3d0"; +} +.ph.ph-play-circle:before { + content: "\e3d2"; +} +.ph.ph-play-pause:before { + content: "\e8be"; +} +.ph.ph-playlist:before { + content: "\e6aa"; +} +.ph.ph-plug:before { + content: "\e946"; +} +.ph.ph-plug-charging:before { + content: "\eb5c"; +} +.ph.ph-plugs:before { + content: "\eb56"; +} +.ph.ph-plugs-connected:before { + content: "\eb5a"; +} +.ph.ph-plus:before { + content: "\e3d4"; +} +.ph.ph-plus-circle:before { + content: "\e3d6"; +} +.ph.ph-plus-minus:before { + content: "\e3d8"; +} +.ph.ph-plus-square:before { + content: "\ed4a"; +} +.ph.ph-poker-chip:before { + content: "\e594"; +} +.ph.ph-police-car:before { + content: "\ec4a"; +} +.ph.ph-polygon:before { + content: "\e6d0"; +} +.ph.ph-popcorn:before { + content: "\eb4e"; +} +.ph.ph-popsicle:before { + content: "\ebbe"; +} +.ph.ph-potted-plant:before { + content: "\ec22"; +} +.ph.ph-power:before { + content: "\e3da"; +} +.ph.ph-prescription:before { + content: "\e7a2"; +} +.ph.ph-presentation:before { + content: "\e654"; +} +.ph.ph-presentation-chart:before { + content: "\e656"; +} +.ph.ph-printer:before { + content: "\e3dc"; +} +.ph.ph-prohibit:before { + content: "\e3de"; +} +.ph.ph-prohibit-inset:before { + content: "\e3e0"; +} +.ph.ph-projector-screen:before { + content: "\e658"; +} +.ph.ph-projector-screen-chart:before { + content: "\e65a"; +} +.ph.ph-pulse:before { + content: "\e000"; +} +.ph.ph-activity:before { + content: "\e000"; +} +.ph.ph-push-pin:before { + content: "\e3e2"; +} +.ph.ph-push-pin-simple:before { + content: "\e65c"; +} +.ph.ph-push-pin-simple-slash:before { + content: "\e65e"; +} +.ph.ph-push-pin-slash:before { + content: "\e3e4"; +} +.ph.ph-puzzle-piece:before { + content: "\e596"; +} +.ph.ph-qr-code:before { + content: "\e3e6"; +} +.ph.ph-question:before { + content: "\e3e8"; +} +.ph.ph-question-mark:before { + content: "\e3e9"; +} +.ph.ph-queue:before { + content: "\e6ac"; +} +.ph.ph-quotes:before { + content: "\e660"; +} +.ph.ph-rabbit:before { + content: "\eac2"; +} +.ph.ph-racquet:before { + content: "\ee02"; +} +.ph.ph-radical:before { + content: "\e3ea"; +} +.ph.ph-radio:before { + content: "\e77e"; +} +.ph.ph-radio-button:before { + content: "\eb08"; +} +.ph.ph-radioactive:before { + content: "\e9dc"; +} +.ph.ph-rainbow:before { + content: "\e598"; +} +.ph.ph-rainbow-cloud:before { + content: "\e59a"; +} +.ph.ph-ranking:before { + content: "\ed62"; +} +.ph.ph-read-cv-logo:before { + content: "\ed0c"; +} +.ph.ph-receipt:before { + content: "\e3ec"; +} +.ph.ph-receipt-x:before { + content: "\ed40"; +} +.ph.ph-record:before { + content: "\e3ee"; +} +.ph.ph-rectangle:before { + content: "\e3f0"; +} +.ph.ph-rectangle-dashed:before { + content: "\e3f2"; +} +.ph.ph-recycle:before { + content: "\e75a"; +} +.ph.ph-reddit-logo:before { + content: "\e59c"; +} +.ph.ph-repeat:before { + content: "\e3f6"; +} +.ph.ph-repeat-once:before { + content: "\e3f8"; +} +.ph.ph-replit-logo:before { + content: "\eb8a"; +} +.ph.ph-resize:before { + content: "\ed6e"; +} +.ph.ph-rewind:before { + content: "\e6a8"; +} +.ph.ph-rewind-circle:before { + content: "\e3fa"; +} +.ph.ph-road-horizon:before { + content: "\e838"; +} +.ph.ph-robot:before { + content: "\e762"; +} +.ph.ph-rocket:before { + content: "\e3fc"; +} +.ph.ph-rocket-launch:before { + content: "\e3fe"; +} +.ph.ph-rows:before { + content: "\e5a2"; +} +.ph.ph-rows-plus-bottom:before { + content: "\e59e"; +} +.ph.ph-rows-plus-top:before { + content: "\e5a0"; +} +.ph.ph-rss:before { + content: "\e400"; +} +.ph.ph-rss-simple:before { + content: "\e402"; +} +.ph.ph-rug:before { + content: "\ea1a"; +} +.ph.ph-ruler:before { + content: "\e6b8"; +} +.ph.ph-sailboat:before { + content: "\e78a"; +} +.ph.ph-scales:before { + content: "\e750"; +} +.ph.ph-scan:before { + content: "\ebb6"; +} +.ph.ph-scan-smiley:before { + content: "\ebb4"; +} +.ph.ph-scissors:before { + content: "\eae0"; +} +.ph.ph-scooter:before { + content: "\e820"; +} +.ph.ph-screencast:before { + content: "\e404"; +} +.ph.ph-screwdriver:before { + content: "\e86e"; +} +.ph.ph-scribble:before { + content: "\e806"; +} +.ph.ph-scribble-loop:before { + content: "\e662"; +} +.ph.ph-scroll:before { + content: "\eb7a"; +} +.ph.ph-seal:before { + content: "\e604"; +} +.ph.ph-circle-wavy:before { + content: "\e604"; +} +.ph.ph-seal-check:before { + content: "\e606"; +} +.ph.ph-circle-wavy-check:before { + content: "\e606"; +} +.ph.ph-seal-percent:before { + content: "\e60a"; +} +.ph.ph-seal-question:before { + content: "\e608"; +} +.ph.ph-circle-wavy-question:before { + content: "\e608"; +} +.ph.ph-seal-warning:before { + content: "\e60c"; +} +.ph.ph-circle-wavy-warning:before { + content: "\e60c"; +} +.ph.ph-seat:before { + content: "\eb8e"; +} +.ph.ph-seatbelt:before { + content: "\edfe"; +} +.ph.ph-security-camera:before { + content: "\eca4"; +} +.ph.ph-selection:before { + content: "\e69a"; +} +.ph.ph-selection-all:before { + content: "\e746"; +} +.ph.ph-selection-background:before { + content: "\eaf8"; +} +.ph.ph-selection-foreground:before { + content: "\eaf6"; +} +.ph.ph-selection-inverse:before { + content: "\e744"; +} +.ph.ph-selection-plus:before { + content: "\e69c"; +} +.ph.ph-selection-slash:before { + content: "\e69e"; +} +.ph.ph-shapes:before { + content: "\ec5e"; +} +.ph.ph-share:before { + content: "\e406"; +} +.ph.ph-share-fat:before { + content: "\ed52"; +} +.ph.ph-share-network:before { + content: "\e408"; +} +.ph.ph-shield:before { + content: "\e40a"; +} +.ph.ph-shield-check:before { + content: "\e40c"; +} +.ph.ph-shield-checkered:before { + content: "\e708"; +} +.ph.ph-shield-chevron:before { + content: "\e40e"; +} +.ph.ph-shield-plus:before { + content: "\e706"; +} +.ph.ph-shield-slash:before { + content: "\e410"; +} +.ph.ph-shield-star:before { + content: "\ec34"; +} +.ph.ph-shield-warning:before { + content: "\e412"; +} +.ph.ph-shipping-container:before { + content: "\e78c"; +} +.ph.ph-shirt-folded:before { + content: "\ea92"; +} +.ph.ph-shooting-star:before { + content: "\ecfa"; +} +.ph.ph-shopping-bag:before { + content: "\e416"; +} +.ph.ph-shopping-bag-open:before { + content: "\e418"; +} +.ph.ph-shopping-cart:before { + content: "\e41e"; +} +.ph.ph-shopping-cart-simple:before { + content: "\e420"; +} +.ph.ph-shovel:before { + content: "\e9e6"; +} +.ph.ph-shower:before { + content: "\e776"; +} +.ph.ph-shrimp:before { + content: "\eab4"; +} +.ph.ph-shuffle:before { + content: "\e422"; +} +.ph.ph-shuffle-angular:before { + content: "\e424"; +} +.ph.ph-shuffle-simple:before { + content: "\e426"; +} +.ph.ph-sidebar:before { + content: "\eab6"; +} +.ph.ph-sidebar-simple:before { + content: "\ec24"; +} +.ph.ph-sigma:before { + content: "\eab8"; +} +.ph.ph-sign-in:before { + content: "\e428"; +} +.ph.ph-sign-out:before { + content: "\e42a"; +} +.ph.ph-signature:before { + content: "\ebac"; +} +.ph.ph-signpost:before { + content: "\e89c"; +} +.ph.ph-sim-card:before { + content: "\e664"; +} +.ph.ph-siren:before { + content: "\e9b8"; +} +.ph.ph-sketch-logo:before { + content: "\e42c"; +} +.ph.ph-skip-back:before { + content: "\e5a4"; +} +.ph.ph-skip-back-circle:before { + content: "\e42e"; +} +.ph.ph-skip-forward:before { + content: "\e5a6"; +} +.ph.ph-skip-forward-circle:before { + content: "\e430"; +} +.ph.ph-skull:before { + content: "\e916"; +} +.ph.ph-skype-logo:before { + content: "\e8dc"; +} +.ph.ph-slack-logo:before { + content: "\e5a8"; +} +.ph.ph-sliders:before { + content: "\e432"; +} +.ph.ph-sliders-horizontal:before { + content: "\e434"; +} +.ph.ph-slideshow:before { + content: "\ed32"; +} +.ph.ph-smiley:before { + content: "\e436"; +} +.ph.ph-smiley-angry:before { + content: "\ec62"; +} +.ph.ph-smiley-blank:before { + content: "\e438"; +} +.ph.ph-smiley-meh:before { + content: "\e43a"; +} +.ph.ph-smiley-melting:before { + content: "\ee56"; +} +.ph.ph-smiley-nervous:before { + content: "\e43c"; +} +.ph.ph-smiley-sad:before { + content: "\e43e"; +} +.ph.ph-smiley-sticker:before { + content: "\e440"; +} +.ph.ph-smiley-wink:before { + content: "\e666"; +} +.ph.ph-smiley-x-eyes:before { + content: "\e442"; +} +.ph.ph-snapchat-logo:before { + content: "\e668"; +} +.ph.ph-sneaker:before { + content: "\e80c"; +} +.ph.ph-sneaker-move:before { + content: "\ed60"; +} +.ph.ph-snowflake:before { + content: "\e5aa"; +} +.ph.ph-soccer-ball:before { + content: "\e716"; +} +.ph.ph-sock:before { + content: "\ecce"; +} +.ph.ph-solar-panel:before { + content: "\ed7a"; +} +.ph.ph-solar-roof:before { + content: "\ed7b"; +} +.ph.ph-sort-ascending:before { + content: "\e444"; +} +.ph.ph-sort-descending:before { + content: "\e446"; +} +.ph.ph-soundcloud-logo:before { + content: "\e8de"; +} +.ph.ph-spade:before { + content: "\e448"; +} +.ph.ph-sparkle:before { + content: "\e6a2"; +} +.ph.ph-speaker-hifi:before { + content: "\ea08"; +} +.ph.ph-speaker-high:before { + content: "\e44a"; +} +.ph.ph-speaker-low:before { + content: "\e44c"; +} +.ph.ph-speaker-none:before { + content: "\e44e"; +} +.ph.ph-speaker-simple-high:before { + content: "\e450"; +} +.ph.ph-speaker-simple-low:before { + content: "\e452"; +} +.ph.ph-speaker-simple-none:before { + content: "\e454"; +} +.ph.ph-speaker-simple-slash:before { + content: "\e456"; +} +.ph.ph-speaker-simple-x:before { + content: "\e458"; +} +.ph.ph-speaker-slash:before { + content: "\e45a"; +} +.ph.ph-speaker-x:before { + content: "\e45c"; +} +.ph.ph-speedometer:before { + content: "\ee74"; +} +.ph.ph-sphere:before { + content: "\ee66"; +} +.ph.ph-spinner:before { + content: "\e66a"; +} +.ph.ph-spinner-ball:before { + content: "\ee28"; +} +.ph.ph-spinner-gap:before { + content: "\e66c"; +} +.ph.ph-spiral:before { + content: "\e9fa"; +} +.ph.ph-split-horizontal:before { + content: "\e872"; +} +.ph.ph-split-vertical:before { + content: "\e876"; +} +.ph.ph-spotify-logo:before { + content: "\e66e"; +} +.ph.ph-spray-bottle:before { + content: "\e7e4"; +} +.ph.ph-square:before { + content: "\e45e"; +} +.ph.ph-square-half:before { + content: "\e462"; +} +.ph.ph-square-half-bottom:before { + content: "\eb16"; +} +.ph.ph-square-logo:before { + content: "\e690"; +} +.ph.ph-square-split-horizontal:before { + content: "\e870"; +} +.ph.ph-square-split-vertical:before { + content: "\e874"; +} +.ph.ph-squares-four:before { + content: "\e464"; +} +.ph.ph-stack:before { + content: "\e466"; +} +.ph.ph-stack-minus:before { + content: "\edf4"; +} +.ph.ph-stack-overflow-logo:before { + content: "\eb78"; +} +.ph.ph-stack-plus:before { + content: "\edf6"; +} +.ph.ph-stack-simple:before { + content: "\e468"; +} +.ph.ph-stairs:before { + content: "\e8ec"; +} +.ph.ph-stamp:before { + content: "\ea48"; +} +.ph.ph-standard-definition:before { + content: "\ea90"; +} +.ph.ph-star:before { + content: "\e46a"; +} +.ph.ph-star-and-crescent:before { + content: "\ecf4"; +} +.ph.ph-star-four:before { + content: "\e6a4"; +} +.ph.ph-star-half:before { + content: "\e70a"; +} +.ph.ph-star-of-david:before { + content: "\e89e"; +} +.ph.ph-steam-logo:before { + content: "\ead4"; +} +.ph.ph-steering-wheel:before { + content: "\e9ac"; +} +.ph.ph-steps:before { + content: "\ecbe"; +} +.ph.ph-stethoscope:before { + content: "\e7ea"; +} +.ph.ph-sticker:before { + content: "\e5ac"; +} +.ph.ph-stool:before { + content: "\ea44"; +} +.ph.ph-stop:before { + content: "\e46c"; +} +.ph.ph-stop-circle:before { + content: "\e46e"; +} +.ph.ph-storefront:before { + content: "\e470"; +} +.ph.ph-strategy:before { + content: "\ea3a"; +} +.ph.ph-stripe-logo:before { + content: "\e698"; +} +.ph.ph-student:before { + content: "\e73e"; +} +.ph.ph-subset-of:before { + content: "\edc0"; +} +.ph.ph-subset-proper-of:before { + content: "\edb6"; +} +.ph.ph-subtitles:before { + content: "\e1a8"; +} +.ph.ph-subtitles-slash:before { + content: "\e1a6"; +} +.ph.ph-subtract:before { + content: "\ebd6"; +} +.ph.ph-subtract-square:before { + content: "\ebd4"; +} +.ph.ph-subway:before { + content: "\e498"; +} +.ph.ph-suitcase:before { + content: "\e5ae"; +} +.ph.ph-suitcase-rolling:before { + content: "\e9b0"; +} +.ph.ph-suitcase-simple:before { + content: "\e5b0"; +} +.ph.ph-sun:before { + content: "\e472"; +} +.ph.ph-sun-dim:before { + content: "\e474"; +} +.ph.ph-sun-horizon:before { + content: "\e5b6"; +} +.ph.ph-sunglasses:before { + content: "\e816"; +} +.ph.ph-superset-of:before { + content: "\edb8"; +} +.ph.ph-superset-proper-of:before { + content: "\edb4"; +} +.ph.ph-swap:before { + content: "\e83c"; +} +.ph.ph-swatches:before { + content: "\e5b8"; +} +.ph.ph-swimming-pool:before { + content: "\ecb6"; +} +.ph.ph-sword:before { + content: "\e5ba"; +} +.ph.ph-synagogue:before { + content: "\ecec"; +} +.ph.ph-syringe:before { + content: "\e968"; +} +.ph.ph-t-shirt:before { + content: "\e670"; +} +.ph.ph-table:before { + content: "\e476"; +} +.ph.ph-tabs:before { + content: "\e778"; +} +.ph.ph-tag:before { + content: "\e478"; +} +.ph.ph-tag-chevron:before { + content: "\e672"; +} +.ph.ph-tag-simple:before { + content: "\e47a"; +} +.ph.ph-target:before { + content: "\e47c"; +} +.ph.ph-taxi:before { + content: "\e902"; +} +.ph.ph-tea-bag:before { + content: "\e8e6"; +} +.ph.ph-telegram-logo:before { + content: "\e5bc"; +} +.ph.ph-television:before { + content: "\e754"; +} +.ph.ph-television-simple:before { + content: "\eae6"; +} +.ph.ph-tennis-ball:before { + content: "\e720"; +} +.ph.ph-tent:before { + content: "\e8ba"; +} +.ph.ph-terminal:before { + content: "\e47e"; +} +.ph.ph-terminal-window:before { + content: "\eae8"; +} +.ph.ph-test-tube:before { + content: "\e7a0"; +} +.ph.ph-text-a-underline:before { + content: "\ed34"; +} +.ph.ph-text-aa:before { + content: "\e6ee"; +} +.ph.ph-text-align-center:before { + content: "\e480"; +} +.ph.ph-text-align-justify:before { + content: "\e482"; +} +.ph.ph-text-align-left:before { + content: "\e484"; +} +.ph.ph-text-align-right:before { + content: "\e486"; +} +.ph.ph-text-b:before { + content: "\e5be"; +} +.ph.ph-text-bolder:before { + content: "\e5be"; +} +.ph.ph-text-columns:before { + content: "\ec96"; +} +.ph.ph-text-h:before { + content: "\e6ba"; +} +.ph.ph-text-h-five:before { + content: "\e6c4"; +} +.ph.ph-text-h-four:before { + content: "\e6c2"; +} +.ph.ph-text-h-one:before { + content: "\e6bc"; +} +.ph.ph-text-h-six:before { + content: "\e6c6"; +} +.ph.ph-text-h-three:before { + content: "\e6c0"; +} +.ph.ph-text-h-two:before { + content: "\e6be"; +} +.ph.ph-text-indent:before { + content: "\ea1e"; +} +.ph.ph-text-italic:before { + content: "\e5c0"; +} +.ph.ph-text-outdent:before { + content: "\ea1c"; +} +.ph.ph-text-strikethrough:before { + content: "\e5c2"; +} +.ph.ph-text-subscript:before { + content: "\ec98"; +} +.ph.ph-text-superscript:before { + content: "\ec9a"; +} +.ph.ph-text-t:before { + content: "\e48a"; +} +.ph.ph-text-t-slash:before { + content: "\e488"; +} +.ph.ph-text-underline:before { + content: "\e5c4"; +} +.ph.ph-textbox:before { + content: "\eb0a"; +} +.ph.ph-thermometer:before { + content: "\e5c6"; +} +.ph.ph-thermometer-cold:before { + content: "\e5c8"; +} +.ph.ph-thermometer-hot:before { + content: "\e5ca"; +} +.ph.ph-thermometer-simple:before { + content: "\e5cc"; +} +.ph.ph-threads-logo:before { + content: "\ed9e"; +} +.ph.ph-three-d:before { + content: "\ea5a"; +} +.ph.ph-thumbs-down:before { + content: "\e48c"; +} +.ph.ph-thumbs-up:before { + content: "\e48e"; +} +.ph.ph-ticket:before { + content: "\e490"; +} +.ph.ph-tidal-logo:before { + content: "\ed1c"; +} +.ph.ph-tiktok-logo:before { + content: "\eaf2"; +} +.ph.ph-tilde:before { + content: "\eda8"; +} +.ph.ph-timer:before { + content: "\e492"; +} +.ph.ph-tip-jar:before { + content: "\e7e2"; +} +.ph.ph-tipi:before { + content: "\ed30"; +} +.ph.ph-tire:before { + content: "\edd2"; +} +.ph.ph-toggle-left:before { + content: "\e674"; +} +.ph.ph-toggle-right:before { + content: "\e676"; +} +.ph.ph-toilet:before { + content: "\e79a"; +} +.ph.ph-toilet-paper:before { + content: "\e79c"; +} +.ph.ph-toolbox:before { + content: "\eca0"; +} +.ph.ph-tooth:before { + content: "\e9cc"; +} +.ph.ph-tornado:before { + content: "\e88c"; +} +.ph.ph-tote:before { + content: "\e494"; +} +.ph.ph-tote-simple:before { + content: "\e678"; +} +.ph.ph-towel:before { + content: "\ede6"; +} +.ph.ph-tractor:before { + content: "\ec6e"; +} +.ph.ph-trademark:before { + content: "\e9f0"; +} +.ph.ph-trademark-registered:before { + content: "\e3f4"; +} +.ph.ph-traffic-cone:before { + content: "\e9a8"; +} +.ph.ph-traffic-sign:before { + content: "\e67a"; +} +.ph.ph-traffic-signal:before { + content: "\e9aa"; +} +.ph.ph-train:before { + content: "\e496"; +} +.ph.ph-train-regional:before { + content: "\e49e"; +} +.ph.ph-train-simple:before { + content: "\e4a0"; +} +.ph.ph-tram:before { + content: "\e9ec"; +} +.ph.ph-translate:before { + content: "\e4a2"; +} +.ph.ph-trash:before { + content: "\e4a6"; +} +.ph.ph-trash-simple:before { + content: "\e4a8"; +} +.ph.ph-tray:before { + content: "\e4aa"; +} +.ph.ph-tray-arrow-down:before { + content: "\e010"; +} +.ph.ph-archive-tray:before { + content: "\e010"; +} +.ph.ph-tray-arrow-up:before { + content: "\ee52"; +} +.ph.ph-treasure-chest:before { + content: "\ede2"; +} +.ph.ph-tree:before { + content: "\e6da"; +} +.ph.ph-tree-evergreen:before { + content: "\e6dc"; +} +.ph.ph-tree-palm:before { + content: "\e91a"; +} +.ph.ph-tree-structure:before { + content: "\e67c"; +} +.ph.ph-tree-view:before { + content: "\ee48"; +} +.ph.ph-trend-down:before { + content: "\e4ac"; +} +.ph.ph-trend-up:before { + content: "\e4ae"; +} +.ph.ph-triangle:before { + content: "\e4b0"; +} +.ph.ph-triangle-dashed:before { + content: "\e4b2"; +} +.ph.ph-trolley:before { + content: "\e5b2"; +} +.ph.ph-trolley-suitcase:before { + content: "\e5b4"; +} +.ph.ph-trophy:before { + content: "\e67e"; +} +.ph.ph-truck:before { + content: "\e4b4"; +} +.ph.ph-truck-trailer:before { + content: "\e4b6"; +} +.ph.ph-tumblr-logo:before { + content: "\e8d4"; +} +.ph.ph-twitch-logo:before { + content: "\e5ce"; +} +.ph.ph-twitter-logo:before { + content: "\e4ba"; +} +.ph.ph-umbrella:before { + content: "\e684"; +} +.ph.ph-umbrella-simple:before { + content: "\e686"; +} +.ph.ph-union:before { + content: "\edbe"; +} +.ph.ph-unite:before { + content: "\e87e"; +} +.ph.ph-unite-square:before { + content: "\e878"; +} +.ph.ph-upload:before { + content: "\e4be"; +} +.ph.ph-upload-simple:before { + content: "\e4c0"; +} +.ph.ph-usb:before { + content: "\e956"; +} +.ph.ph-user:before { + content: "\e4c2"; +} +.ph.ph-user-check:before { + content: "\eafa"; +} +.ph.ph-user-circle:before { + content: "\e4c4"; +} +.ph.ph-user-circle-check:before { + content: "\ec38"; +} +.ph.ph-user-circle-dashed:before { + content: "\ec36"; +} +.ph.ph-user-circle-gear:before { + content: "\e4c6"; +} +.ph.ph-user-circle-minus:before { + content: "\e4c8"; +} +.ph.ph-user-circle-plus:before { + content: "\e4ca"; +} +.ph.ph-user-focus:before { + content: "\e6fc"; +} +.ph.ph-user-gear:before { + content: "\e4cc"; +} +.ph.ph-user-list:before { + content: "\e73c"; +} +.ph.ph-user-minus:before { + content: "\e4ce"; +} +.ph.ph-user-plus:before { + content: "\e4d0"; +} +.ph.ph-user-rectangle:before { + content: "\e4d2"; +} +.ph.ph-user-sound:before { + content: "\eca8"; +} +.ph.ph-user-square:before { + content: "\e4d4"; +} +.ph.ph-user-switch:before { + content: "\e756"; +} +.ph.ph-users:before { + content: "\e4d6"; +} +.ph.ph-users-four:before { + content: "\e68c"; +} +.ph.ph-users-three:before { + content: "\e68e"; +} +.ph.ph-van:before { + content: "\e826"; +} +.ph.ph-vault:before { + content: "\e76e"; +} +.ph.ph-vector-three:before { + content: "\ee62"; +} +.ph.ph-vector-two:before { + content: "\ee64"; +} +.ph.ph-vibrate:before { + content: "\e4d8"; +} +.ph.ph-video:before { + content: "\e740"; +} +.ph.ph-video-camera:before { + content: "\e4da"; +} +.ph.ph-video-camera-slash:before { + content: "\e4dc"; +} +.ph.ph-video-conference:before { + content: "\edce"; +} +.ph.ph-vignette:before { + content: "\eba2"; +} +.ph.ph-vinyl-record:before { + content: "\ecac"; +} +.ph.ph-virtual-reality:before { + content: "\e7b8"; +} +.ph.ph-virus:before { + content: "\e7d6"; +} +.ph.ph-visor:before { + content: "\ee2a"; +} +.ph.ph-voicemail:before { + content: "\e4de"; +} +.ph.ph-volleyball:before { + content: "\e726"; +} +.ph.ph-wall:before { + content: "\e688"; +} +.ph.ph-wallet:before { + content: "\e68a"; +} +.ph.ph-warehouse:before { + content: "\ecd4"; +} +.ph.ph-warning:before { + content: "\e4e0"; +} +.ph.ph-warning-circle:before { + content: "\e4e2"; +} +.ph.ph-warning-diamond:before { + content: "\e7fc"; +} +.ph.ph-warning-octagon:before { + content: "\e4e4"; +} +.ph.ph-washing-machine:before { + content: "\ede8"; +} +.ph.ph-watch:before { + content: "\e4e6"; +} +.ph.ph-wave-sawtooth:before { + content: "\ea9c"; +} +.ph.ph-wave-sine:before { + content: "\ea9a"; +} +.ph.ph-wave-square:before { + content: "\ea9e"; +} +.ph.ph-wave-triangle:before { + content: "\eaa0"; +} +.ph.ph-waveform:before { + content: "\e802"; +} +.ph.ph-waveform-slash:before { + content: "\e800"; +} +.ph.ph-waves:before { + content: "\e6de"; +} +.ph.ph-webcam:before { + content: "\e9b2"; +} +.ph.ph-webcam-slash:before { + content: "\ecdc"; +} +.ph.ph-webhooks-logo:before { + content: "\ecae"; +} +.ph.ph-wechat-logo:before { + content: "\e8d2"; +} +.ph.ph-whatsapp-logo:before { + content: "\e5d0"; +} +.ph.ph-wheelchair:before { + content: "\e4e8"; +} +.ph.ph-wheelchair-motion:before { + content: "\e89a"; +} +.ph.ph-wifi-high:before { + content: "\e4ea"; +} +.ph.ph-wifi-low:before { + content: "\e4ec"; +} +.ph.ph-wifi-medium:before { + content: "\e4ee"; +} +.ph.ph-wifi-none:before { + content: "\e4f0"; +} +.ph.ph-wifi-slash:before { + content: "\e4f2"; +} +.ph.ph-wifi-x:before { + content: "\e4f4"; +} +.ph.ph-wind:before { + content: "\e5d2"; +} +.ph.ph-windmill:before { + content: "\e9f8"; +} +.ph.ph-windows-logo:before { + content: "\e692"; +} +.ph.ph-wine:before { + content: "\e6b2"; +} +.ph.ph-wrench:before { + content: "\e5d4"; +} +.ph.ph-x:before { + content: "\e4f6"; +} +.ph.ph-x-circle:before { + content: "\e4f8"; +} +.ph.ph-x-logo:before { + content: "\e4bc"; +} +.ph.ph-x-square:before { + content: "\e4fa"; +} +.ph.ph-yarn:before { + content: "\ed9a"; +} +.ph.ph-yin-yang:before { + content: "\e92a"; +} +.ph.ph-youtube-logo:before { + content: "\e4fc"; +} diff --git a/scripts/add-button-arrows.js b/scripts/add-button-arrows.js new file mode 100644 index 0000000..2b79920 --- /dev/null +++ b/scripts/add-button-arrows.js @@ -0,0 +1,40 @@ +const fs = require("fs"); +const path = require("path"); + +const htmlPath = path.join(__dirname, "..", "demo", "figma", "buttons.html"); +let html = fs.readFileSync(htmlPath, "utf8"); + +const leftArrow = ``; +const rightArrow = ``; + +// Replace text-only buttons (not icon-only) that contain exactly "Button" +html = html.replace( + /]*?)class="btn ([^"]*?)"([^>]*?)>Button<\/button>/g, + (match, before, classes, after) => { + if (classes.includes("btn-icon-only")) return match; + const newClasses = classes.trim() + " btn-arrows"; + return `${leftArrow}Button${rightArrow}`; + } +); + +// Add CSS for btn-arrow padding overrides +const arrowCss = ` + .btn-arrows.btn-lg { padding: 14px 20px; } + .btn-arrows.btn-md { padding: 10px 16px; } + .btn-arrows.btn-sm { padding: 7px 12px; } + .btn-arrows.btn-xs { padding: 5px 10px; } + .btn-arrows.btn-xxs { padding: 2px 8px; } + .btn-arrow { flex-shrink: 0; width: 1em; height: 1em; } + .btn-lg .btn-arrow { width: 18px; height: 18px; } + .btn-md .btn-arrow { width: 15px; height: 15px; } + .btn-sm .btn-arrow { width: 13px; height: 13px; } + .btn-xs .btn-arrow { width: 12px; height: 12px; } + .btn-xxs .btn-arrow { width: 12px; height: 12px; } +`; +html = html.replace( + /\.figma-row \{ display: flex; align-items: center; gap: 12px; margin-bottom: 16px; \}/, + match => match + arrowCss +); + +fs.writeFileSync(htmlPath, html); +console.log("Added arrow icons to text buttons in buttons.html"); diff --git a/scripts/analyze-baseline.js b/scripts/analyze-baseline.js new file mode 100644 index 0000000..eec48b8 --- /dev/null +++ b/scripts/analyze-baseline.js @@ -0,0 +1,60 @@ +const sharp = require("sharp"); +const path = require("path"); + +const baselinePath = path.join(__dirname, "..", "tests", "figma-baselines", "alerts-baseline.png"); + +async function analyze() { + const { data, info } = await sharp(baselinePath).ensureAlpha().raw().toBuffer({ resolveWithObject: true }); + const { width, height } = info; + + function isFramePurple(r, g, b) { return b > 100 && r > 80 && g < 100; } + function isWhite(r, g, b) { return r > 245 && g > 245 && b > 245; } + function isBlackText(r, g, b) { return r < 60 && g < 60 && b < 60; } + function isContent(r, g, b, a) { return a > 20 && !isFramePurple(r, g, b) && !isWhite(r, g, b); } + + // Find alert card blocks by rows with non-white content + const rowHasContent = []; + for (let y = 0; y < height; y++) { + let has = false; + for (let x = 0; x < width; x++) { + const idx = (y * width + x) * 4; + if (isContent(data[idx], data[idx+1], data[idx+2], data[idx+3])) { has = true; break; } + } + rowHasContent[y] = has; + } + + const blocks = []; let start = null; + for (let y = 0; y < height; y++) { + if (rowHasContent[y] && start === null) start = y; + if (!rowHasContent[y] && start !== null) { blocks.push({ start, end: y - 1, height: y - start }); start = null; } + } + if (start !== null) blocks.push({ start, end: height - 1, height: height - start }); + + console.log("Image:", width, height); + console.log("Content blocks:"); + blocks.forEach((b, i) => console.log(` ${i}: y ${b.start}-${b.end}, h ${b.height}`)); + + // For first alert card block, find left/right edges + if (blocks.length > 0) { + const b = blocks[0]; + const midY = Math.floor((b.start + b.end) / 2); + let left = width, right = -1; + for (let x = 0; x < width; x++) { + const idx = (midY * width + x) * 4; + if (isContent(data[idx], data[idx+1], data[idx+2], data[idx+3])) { + left = Math.min(left, x); right = Math.max(right, x); + } + } + console.log(`Block 0 midY ${midY}: left ${left}, right ${right}, width ${right - left + 1}`); + + // Sample colors at left, middle, right + const sampleX = [left + 20, Math.floor((left + right) / 2), right - 20]; + for (const x of sampleX) { + const idx = (midY * width + x) * 4; + const hex = `#${[data[idx], data[idx+1], data[idx+2]].map(v => v.toString(16).padStart(2,"0")).join("")}`; + console.log(` Color at x ${x}: ${hex}`); + } + } +} + +analyze().catch(console.error); diff --git a/scripts/report-figma-diff.js b/scripts/report-figma-diff.js new file mode 100644 index 0000000..5e38d45 --- /dev/null +++ b/scripts/report-figma-diff.js @@ -0,0 +1,72 @@ +const fs = require("fs"); +const path = require("path"); +const pixelmatch = (require("pixelmatch").default || require("pixelmatch")); +const sharp = require("sharp"); + +const BASELINES_DIR = path.join(__dirname, "..", "tests", "figma-baselines"); +const SCREENSHOTS_DIR = path.join(__dirname, "..", "tests", "figma-screenshots"); + +const comparisons = [ + { name: "buttons", viewport: { width: 1200, height: 2372 }, background: "#ffffff" }, + { name: "alerts", viewport: { width: 1200, height: 1363 }, background: "#ffffff" }, + { name: "badges", viewport: { width: 1200, height: 743 }, background: "#ffffff" }, + { name: "forms", viewport: { width: 1200, height: 2648 }, background: "#ffffff" }, + { name: "breadcrumbs", viewport: { width: 1200, height: 567 }, background: "#000000" }, + { name: "divider", viewport: { width: 1200, height: 120 }, background: "#ffffff" }, + { name: "attached-file", viewport: { width: 560, height: 2000 }, background: "#000000" }, +]; + +async function normalizeToCanvas(imagePath, targetWidth, targetHeight, padColor) { + const { data, info } = await sharp(imagePath) + .ensureAlpha() + .resize(targetWidth, targetHeight, { fit: "inside" }) + .raw() + .toBuffer({ resolveWithObject: true }); + const { width, height } = info; + const canvas = Buffer.alloc(targetWidth * targetHeight * 4); + const hex = padColor.replace("#", ""); + const pr = parseInt(hex.substring(0, 2), 16); + const pg = parseInt(hex.substring(2, 4), 16); + const pb = parseInt(hex.substring(4, 6), 16); + for (let i = 0; i < targetWidth * targetHeight; i++) { + canvas[i * 4] = pr; + canvas[i * 4 + 1] = pg; + canvas[i * 4 + 2] = pb; + canvas[i * 4 + 3] = 255; + } + const offsetX = Math.round((targetWidth - width) / 2); + const offsetY = Math.round((targetHeight - height) / 2); + for (let y = 0; y < height; y++) { + for (let x = 0; x < width; x++) { + const srcIdx = (y * width + x) * 4; + const dstIdx = ((y + offsetY) * targetWidth + (x + offsetX)) * 4; + const srcA = data[srcIdx + 3] / 255; + const invA = 1 - srcA; + canvas[dstIdx] = Math.round(pr * invA + data[srcIdx] * srcA); + canvas[dstIdx + 1] = Math.round(pg * invA + data[srcIdx + 1] * srcA); + canvas[dstIdx + 2] = Math.round(pb * invA + data[srcIdx + 2] * srcA); + canvas[dstIdx + 3] = 255; + } + } + return canvas; +} + +async function main() { + for (const c of comparisons) { + const screenshotPath = path.join(SCREENSHOTS_DIR, `${c.name}.png`); + const baselinePath = path.join(BASELINES_DIR, c.baseline || `${c.name}-baseline.png`); + if (!fs.existsSync(screenshotPath) || !fs.existsSync(baselinePath)) { + console.log(`${c.name}: missing files`); + continue; + } + const tw = c.viewport.width; + const th = c.viewport.height; + const screenshot = await normalizeToCanvas(screenshotPath, tw, th, c.background); + const baseline = await normalizeToCanvas(baselinePath, tw, th, c.background); + const diff = Buffer.alloc(tw * th * 4); + const mismatched = pixelmatch(screenshot, baseline, diff, tw, th, { threshold: 0.1, includeAA: false }); + console.log(`${c.name}: ${(mismatched / (tw * th) * 100).toFixed(2)}%`); + } +} + +main().catch(err => { console.error(err); process.exit(1); }); diff --git a/src/js/index.js b/src/js/index.js index bff3dc0..2a5ed93 100644 --- a/src/js/index.js +++ b/src/js/index.js @@ -20,7 +20,7 @@ import Loader from "./components/loader.js"; const api = { - Icons, + Helper: Icons, Toasts, Tabs, Progress, @@ -34,35 +34,25 @@ Breadcrumbs, Accordion, Popover, - Loader, - Helper: { - iconSvg: Icons.iconSvg, - iconNode: Icons.iconNode, - iconHtml: Icons.iconHtml, - backgroundHtml: Background.html, - backgroundCreate: Background.create, - breadcrumbHtml: Breadcrumbs.html, - breadcrumbCreate: Breadcrumbs.create, - accordionInit: Accordion.init, - accordionExpand: Accordion.expand, - accordionCollapse: Accordion.collapse, - accordionToggle: Accordion.toggle, - popoverInit: Popover.init, - popoverOpen: Popover.open, - popoverClose: Popover.close, - popoverToggle: Popover.toggle, - loaderHtml: Loader.html, - loaderCreate: Loader.create - } + Loader }; if (typeof window !== "undefined") { + window.GNexusUIKit = api; window.VMKUIKit = api; document.addEventListener("DOMContentLoaded", () => { - // Component initialisation hooks will be wired here. + Background.init?.(); + Breadcrumbs.init?.(); + Accordion.init?.(); + Tabs.init?.(); + Dropdowns.init?.(); + Tooltips.init?.(); + Popover.init?.(); + Loader.init?.(); + NavigationShell.init?.(); }); } -export { Icons, Toasts, Tabs, Progress, Tables, Lists, Skeleton, Tooltips, Dropdowns, NavigationShell, Background, Breadcrumbs, Accordion, Popover, Loader }; +export { Icons as Helper, Toasts, Tabs, Progress, Tables, Lists, Skeleton, Tooltips, Dropdowns, NavigationShell, Background, Breadcrumbs, Accordion, Popover, Loader }; export default api; diff --git a/src/scss/components/_accordion.scss b/src/scss/components/_accordion.scss index 9cd8939..14b4657 100644 --- a/src/scss/components/_accordion.scss +++ b/src/scss/components/_accordion.scss @@ -98,6 +98,10 @@ font-family: $font-family-base; font-size: 14px; line-height: 1.5; + transition: + height $motion-base $motion-ease, + opacity $motion-base $motion-ease, + transform $motion-base $motion-ease; > *:first-child { margin-top: 0; diff --git a/src/scss/components/_breadcrumbs.scss b/src/scss/components/_breadcrumbs.scss index 52dd46f..97188bb 100644 --- a/src/scss/components/_breadcrumbs.scss +++ b/src/scss/components/_breadcrumbs.scss @@ -9,12 +9,12 @@ display: flex; align-items: center; flex-wrap: wrap; - gap: $space-2; + gap: $space-3; margin: 0; padding: 0; list-style: none; font-family: $font-family-base; - font-size: 14px; + font-size: 18px; font-weight: $font-weight-semibold; line-height: 1; color: $neutral-700; @@ -94,24 +94,31 @@ } .breadcrumb-separator { - color: $neutral-400; + color: $neutral-500; } .breadcrumb-item.is-active span { display: inline-flex; align-items: center; justify-content: center; - min-height: 36px; - padding: $space-1 $space-4; + min-height: 44px; + padding: $space-2 $space-5; border-radius: $radius-full; background-color: $neutral-0; color: $info-text; } - .breadcrumb-collapse:hover, - .breadcrumb-collapse.is-hover { - color: $neutral-0; - background-color: rgba($neutral-0, 0.12); + .breadcrumb-collapse { + min-height: 44px; + padding: $space-2 $space-3; + border-radius: $radius-full; + font-size: 18px; + + &:hover, + &.is-hover { + color: $neutral-0; + background-color: rgba($neutral-0, 0.12); + } } } diff --git a/src/scss/components/_buttons.scss b/src/scss/components/_buttons.scss index 187a4f1..eb7ea68 100644 --- a/src/scss/components/_buttons.scss +++ b/src/scss/components/_buttons.scss @@ -88,10 +88,16 @@ } } - .vmk-icon { + .vmk-icon, + .btn-arrow { color: currentColor; flex-shrink: 0; } + + .btn-arrow { + width: 1em; + height: 1em; + } } // Sizes @@ -148,13 +154,13 @@ // Primary — filled yellow CTA .btn-primary { - --btn-bg: #{$yellow-300}; - --btn-border: #{$yellow-300}; + --btn-bg: #{$yellow-400}; + --btn-border: #{$yellow-400}; --btn-text: #{$neutral-900}; - --btn-hover-bg: #{$yellow-400}; - --btn-hover-border: #{$yellow-400}; - --btn-focus-bg: #{$yellow-500}; - --btn-focus-border: #{$yellow-500}; + --btn-hover-bg: #{$yellow-500}; + --btn-hover-border: #{$yellow-500}; + --btn-focus-bg: #{$yellow-600}; + --btn-focus-border: #{$yellow-600}; --btn-disabled-bg: #{$neutral-200}; --btn-disabled-border: #{$neutral-200}; --btn-disabled-text: #{$neutral-500}; @@ -162,41 +168,45 @@ // Accent — yellow alias, kept for API compatibility. .btn-accent { - --btn-bg: #{$yellow-300}; - --btn-border: #{$yellow-300}; + --btn-bg: #{$yellow-400}; + --btn-border: #{$yellow-400}; --btn-text: #{$neutral-900}; - --btn-hover-bg: #{$yellow-400}; - --btn-hover-border: #{$yellow-400}; - --btn-focus-bg: #{$yellow-500}; - --btn-focus-border: #{$yellow-500}; + --btn-hover-bg: #{$yellow-500}; + --btn-hover-border: #{$yellow-500}; + --btn-focus-bg: #{$yellow-600}; + --btn-focus-border: #{$yellow-600}; --btn-disabled-bg: #{$neutral-200}; --btn-disabled-border: #{$neutral-200}; --btn-disabled-text: #{$neutral-500}; } -// Secondary — mint outline (Figma secondary row) +// Secondary — mint outline/fill (Figma secondary row) .btn-secondary { --btn-bg: transparent; --btn-border: #{$mint-600}; --btn-text: #{$mint-800}; - --btn-hover-bg: #{$mint-100}; + --btn-hover-bg: #{$mint-300}; --btn-hover-border: #{$mint-600}; - --btn-focus-bg: #{$mint-200}; + --btn-hover-text: #{$mint-900}; + --btn-focus-bg: #{$mint-400}; --btn-focus-border: #{$mint-600}; + --btn-focus-text: #{$mint-900}; --btn-disabled-bg: transparent; --btn-disabled-border: #{$mint-200}; - --btn-disabled-text: #{$mint-500}; + --btn-disabled-text: #{$mint-400}; } -// Tertiary — ghost dark text (Figma tertiary row) +// Tertiary — ghost dark teal text (Figma tertiary row) .btn-tertiary { --btn-bg: transparent; --btn-border: transparent; - --btn-text: #{$neutral-900}; + --btn-text: #{$mint-900}; --btn-hover-bg: #{$neutral-100}; --btn-hover-border: transparent; + --btn-hover-text: #{$mint-900}; --btn-focus-bg: #{$neutral-200}; --btn-focus-border: transparent; + --btn-focus-text: #{$mint-900}; --btn-disabled-bg: transparent; --btn-disabled-border: transparent; --btn-disabled-text: #{$neutral-400}; diff --git a/src/scss/components/_description-list.scss b/src/scss/components/_description-list.scss new file mode 100644 index 0000000..83d3d25 --- /dev/null +++ b/src/scss/components/_description-list.scss @@ -0,0 +1,94 @@ +/* ========================= + Description list +========================= */ + +@use "../kit-deps" as *; + +.description-list { + display: grid; + width: 100%; + max-width: 760px; + margin: 0; + border: $border-width-base $border-style-base $border-color-muted; + border-left-width: $border-width-accent; + border-radius: $radius-md; + background-color: $surface-panel-muted; + overflow: hidden; + + .description-list-row { + display: grid; + grid-template-columns: minmax(140px, 0.36fr) minmax(0, 1fr); + gap: $space-4; + padding: $space-3 $space-4; + border-bottom: $border-width-base $border-style-base rgba($neutral-900, 0.06); + transition: background-color $motion-base $motion-ease; + + &:last-child { + border-bottom: 0; + } + + &:hover { + background-color: rgba($neutral-900, 0.03); + + .description-list-term { + color: $mint-700; + } + + .description-list-value { + transform: translateX($space-1); + } + } + } + + .description-list-term { + margin: 0; + color: $neutral-600; + font-size: 13px; + font-weight: $font-weight-semibold; + line-height: 1.5; + text-transform: uppercase; + transition: color $motion-base $motion-ease; + } + + .description-list-value { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: $space-2; + min-width: 0; + margin: 0; + color: $neutral-900; + font-size: 15px; + font-weight: $font-weight-medium; + line-height: 1.5; + transition: transform $motion-base $motion-ease; + } + + .description-list-value-muted { + color: $neutral-500; + } + + &.description-list-compact { + max-width: 520px; + + .description-list-row { + grid-template-columns: minmax(112px, 0.42fr) minmax(0, 1fr); + gap: $space-3; + padding: $space-2 $space-3; + } + + .description-list-term, + .description-list-value { + font-size: 13px; + } + } +} + +@media (max-width: 640px) { + .description-list { + .description-list-row { + grid-template-columns: 1fr; + gap: $space-1; + } + } +} diff --git a/src/scss/components/_drawer.scss b/src/scss/components/_drawer.scss index 15c5481..f1cbea6 100644 --- a/src/scss/components/_drawer.scss +++ b/src/scss/components/_drawer.scss @@ -8,6 +8,14 @@ position: fixed; inset: 0; z-index: 1000; + opacity: 0; + pointer-events: none; + transition: opacity $motion-base $motion-ease; + + &.a-show { + opacity: 1; + pointer-events: auto; + } &.a-show .drawer-backdrop { opacity: 1; diff --git a/src/scss/components/_empty-state.scss b/src/scss/components/_empty-state.scss new file mode 100644 index 0000000..0fc8fb0 --- /dev/null +++ b/src/scss/components/_empty-state.scss @@ -0,0 +1,73 @@ +/* ========================= + Empty state +========================= */ + +@use "../kit-deps" as *; + +.empty-state { + max-width: 560px; + padding: $space-6; + border: $border-width-base $border-style-base $border-color-muted; + border-left-width: $border-width-accent; + border-radius: $radius-md; + background-color: $surface-panel-muted; + text-align: left; + + .empty-state-icon { + display: inline-flex; + align-items: center; + justify-content: center; + width: 56px; + height: 56px; + margin-bottom: $space-4; + color: $neutral-900; + background-color: $yellow-400; + border-radius: $radius-md; + font-size: 24px; + + .vmk-icon { + width: 24px; + height: 24px; + } + } + + .empty-state-title { + margin: 0 0 $space-2; + font-size: 20px; + font-weight: $font-weight-bold; + line-height: 1.3; + color: $neutral-900; + } + + .empty-state-text { + max-width: 440px; + margin: 0 0 $space-5; + color: $neutral-600; + font-size: 15px; + line-height: 1.6; + } + + .empty-state-actions { + display: flex; + flex-wrap: wrap; + gap: $space-3; + } + + &.empty-state-error { + border-color: $danger-text; + + .empty-state-icon { + background-color: $danger-text; + color: $neutral-0; + } + } + + &.empty-state-success { + border-color: $success-text; + + .empty-state-icon { + background-color: $success-text; + color: $neutral-0; + } + } +} diff --git a/src/scss/components/_file-upload.scss b/src/scss/components/_file-upload.scss index 56248d8..725d783 100644 --- a/src/scss/components/_file-upload.scss +++ b/src/scss/components/_file-upload.scss @@ -6,7 +6,7 @@ @use "../kit-deps" as *; -$file-attachment-size: 240px; +$file-attachment-size: 420px; .file-upload-panel { width: 100%; @@ -118,7 +118,7 @@ .file-upload-preview { display: grid; grid-template-columns: repeat(auto-fill, minmax($file-attachment-size, 1fr)); - gap: $space-4; + gap: $space-7; } .file-upload-preview-item, @@ -127,12 +127,11 @@ display: flex; flex-direction: column; justify-content: space-between; - width: 100%; - max-width: $file-attachment-size; - aspect-ratio: 1 / 1; - padding: $space-4; - border: $border-width-base $border-style-base $border-color-muted; - border-radius: $radius-xl; + width: $file-attachment-size; + height: $file-attachment-size; + padding: 0; + border: 0; + border-radius: 40px; background: $neutral-0; color: $neutral-900; box-sizing: border-box; @@ -142,26 +141,27 @@ .file-upload-preview-remove, .file-attachment-remove { position: absolute; - top: $space-3; - right: $space-3; + top: $space-4; + right: $space-4; + z-index: 3; display: inline-flex; align-items: center; justify-content: center; - width: 28px; - height: 28px; + width: 32px; + height: 32px; padding: 0; border: 0; border-radius: $radius-full; background: transparent; color: $neutral-700; - font-size: $icon-size-md; + font-size: 24px; cursor: pointer; transition: color $motion-base $motion-ease, background-color $motion-base $motion-ease; &:hover, &.is-hover { color: $neutral-900; - background: $neutral-100; + background: rgba($neutral-900, 0.06); } &:focus-visible { @@ -178,11 +178,17 @@ justify-content: center; background: transparent; - img { + img, + svg { width: 100%; height: 100%; object-fit: cover; } + + svg { + width: 56px; + height: 56px; + } } .file-upload-preview-type, @@ -207,9 +213,10 @@ z-index: 1; display: block; margin-top: auto; + padding: 0 $space-5 $space-5; color: $neutral-900; font-family: $font-family-base; - font-size: 16px; + font-size: 18px; font-weight: $font-weight-semibold; line-height: 1.3; white-space: nowrap; @@ -231,32 +238,60 @@ .file-attachment-header { position: relative; - z-index: 1; + z-index: 2; display: flex; align-items: flex-start; justify-content: space-between; gap: $space-2; + padding: $space-4 $space-4 $space-3; } .file-attachment-format { display: inline-flex; align-items: center; padding: $space-1 $space-2; - border-radius: $radius-sm; + border-radius: $radius-md; background: $neutral-100; color: $neutral-700; font-family: $font-family-base; - font-size: 12px; - font-weight: $font-weight-semibold; + font-size: 24px; + font-weight: $font-weight-bold; text-transform: uppercase; + line-height: 1; } // State variants +.file-attachment.is-pdf, +.file-upload-preview-item.is-pdf { + .file-attachment-format { + background: $yellow-300; + color: $neutral-900; + } + + .file-attachment-remove { + color: $neutral-900; + } +} + +.file-attachment.is-image, +.file-upload-preview-item.is-image, +.file-attachment.is-placeholder, +.file-upload-preview-item.is-placeholder { + .file-attachment-format { + background: $mint-300; + color: $neutral-900; + } + + .file-attachment-remove { + color: $neutral-900; + } +} + .file-attachment.is-placeholder, .file-upload-preview-item.is-placeholder { .file-attachment-visual, .file-upload-preview-visual { - background: repeating-conic-gradient($neutral-200 0% 25%, $neutral-25 0% 50%) 50% / 50px 50px; + background: repeating-conic-gradient($neutral-100 0% 25%, $neutral-0 0% 50%) 50% / 40px 40px; } } @@ -266,16 +301,25 @@ .file-upload-preview-item.is-retry { background: $neutral-100; + .file-attachment-format { + background: $neutral-200; + color: $neutral-700; + } + + .file-attachment-remove { + color: $neutral-700; + } + .file-attachment-visual, .file-upload-preview-visual { color: $danger-text; - font-size: 48px; + font-size: 56px; &::before { content: ""; position: absolute; - width: 96px; - height: 96px; + width: 112px; + height: 112px; border-radius: $radius-full; background: $neutral-0; z-index: -1; diff --git a/src/scss/components/_input-group.scss b/src/scss/components/_input-group.scss new file mode 100644 index 0000000..589ae17 --- /dev/null +++ b/src/scss/components/_input-group.scss @@ -0,0 +1,112 @@ +/* ========================= + Input group +========================= */ + +@use "../kit-deps" as *; + +.input-group { + display: flex; + align-items: stretch; + width: 100%; + max-width: 600px; + min-height: $control-height-md; + border: $border-width-base $border-style-base $neutral-300; + border-bottom-width: $border-width-accent; + border-radius: $radius-lg; + background-color: $neutral-0; + overflow: hidden; + transition: + border-color $motion-base $motion-ease, + box-shadow $motion-base $motion-ease, + background-color $motion-base $motion-ease; + + &:focus-within { + @include focus_ring; + border-color: $mint-600; + background-color: $neutral-0; + } + + .input-group-addon, + .input-group-action { + display: inline-flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + min-width: $control-height-md; + padding: 0 $space-3; + color: $neutral-700; + background-color: $neutral-50; + border: 0; + font-family: $font-family-base; + font-size: 14px; + font-weight: $font-weight-semibold; + text-transform: uppercase; + + .vmk-icon { + font-size: $icon-size-md; + color: currentColor; + } + } + + .input-group-action { + color: $mint-800; + background-color: $neutral-50; + cursor: pointer; + transition: + color $motion-base $motion-ease, + background-color $motion-base $motion-ease; + + &:hover:not(:disabled):not(.is-disabled) { + background-color: $mint-100; + color: $mint-900; + } + } + + .input-group-input, + input { + flex: 1 1 auto; + min-width: 0; + min-height: auto; + padding: $space-3 $space-4; + color: $neutral-900; + background-color: transparent; + border: 0; + border-radius: 0; + font-family: $font-family-base; + font-size: 15px; + font-weight: $font-weight-medium; + line-height: 1.25; + + &:focus { + outline: none; + } + + &::placeholder { + color: $neutral-500; + } + + &::-webkit-search-cancel-button, + &::-webkit-search-decoration, + &::-webkit-search-results-button, + &::-webkit-search-results-decoration { + display: none; + -webkit-appearance: none; + } + } + + &.input-group-compact { + min-height: $control-height-sm; + border-radius: $radius-md; + + .input-group-addon, + .input-group-action { + min-width: $control-height-sm; + } + + .input-group-input, + input { + padding: $space-2 $space-3; + font-size: 14px; + } + } +} diff --git a/src/scss/components/_modals.scss b/src/scss/components/_modals.scss index 560c5a6..fa102c0 100644 --- a/src/scss/components/_modals.scss +++ b/src/scss/components/_modals.scss @@ -12,6 +12,14 @@ align-items: center; justify-content: center; padding: $space-4; + opacity: 0; + pointer-events: none; + transition: opacity $motion-base $motion-ease; + + &.a-show { + opacity: 1; + pointer-events: auto; + } &.a-show .modal-backdrop { opacity: 1; diff --git a/src/scss/components/_page-header.scss b/src/scss/components/_page-header.scss new file mode 100644 index 0000000..ae46e8b --- /dev/null +++ b/src/scss/components/_page-header.scss @@ -0,0 +1,89 @@ +/* ========================= + Page header +========================= */ + +@use "../kit-deps" as *; + +.page-header { + position: relative; + display: flex; + flex-wrap: wrap; + align-items: flex-end; + justify-content: space-between; + gap: $space-5; + width: 100%; + padding: $space-5; + border: $border-width-base $border-style-base $border-color-muted; + border-left-width: $border-width-accent; + border-radius: $radius-md; + background-color: $surface-panel-muted; + overflow: hidden; + + .page-header-content { + display: flex; + flex-direction: column; + gap: $space-2; + min-width: min(100%, 320px); + } + + .page-header-kicker { + color: $mint-700; + font-size: 12px; + font-weight: $font-weight-bold; + line-height: 1.4; + text-transform: uppercase; + letter-spacing: 0.04em; + } + + .page-header-title { + margin: 0; + color: $neutral-900; + font-size: 28px; + font-weight: $font-weight-bold; + line-height: 1.2; + } + + .page-header-subtitle { + max-width: 720px; + margin: 0; + color: $neutral-600; + font-size: 15px; + line-height: 1.6; + } + + .page-header-meta { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: $space-2; + color: $neutral-500; + font-size: 13px; + } + + .page-header-actions { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: flex-end; + gap: $space-3; + min-width: 0; + } + + &.page-header-compact { + align-items: center; + padding: $space-4; + + .page-header-title { + font-size: 20px; + } + } + + &.page-header-accent { + border-color: $mint-600; + background-color: rgba($mint-300, 0.16); + + .page-header-kicker { + color: $mint-800; + } + } +} diff --git a/src/scss/kit.scss b/src/scss/kit.scss index 47c8895..22eb6ce 100644 --- a/src/scss/kit.scss +++ b/src/scss/kit.scss @@ -7,6 +7,7 @@ @use "components/badges"; @use "components/alerts"; @use "components/forms"; +@use "components/input-group"; @use "components/select"; @use "components/checkbox"; @use "components/radio"; @@ -20,7 +21,9 @@ @use "components/chips"; @use "components/progress"; @use "components/tables"; +@use "components/description-list"; @use "components/breadcrumbs"; +@use "components/page-header"; @use "components/divider"; @use "components/accordion"; @use "components/stepper"; @@ -30,6 +33,7 @@ @use "components/timeline"; @use "components/loader"; @use "components/lists"; +@use "components/empty-state"; @use "components/skeleton"; @use "components/tooltip"; @use "components/dropdown"; diff --git a/src/vue/components/GnDescriptionList.js b/src/vue/components/GnDescriptionList.js new file mode 100644 index 0000000..644dc69 --- /dev/null +++ b/src/vue/components/GnDescriptionList.js @@ -0,0 +1,34 @@ +/** + * GnDescriptionList — term/value pairs. + * + * API-compatible with gnexus-ui-kit GnDescriptionList. + * + * @typedef {Object} GnDescriptionListProps + * @property {Array<{term?: string, label?: string, value?: any, muted?: boolean, key?: string}>} [items=[]] + * @property {boolean} [compact=false] + * + * @slots [item.key] - Custom content for a specific item + */ + +import { defineComponent, h } from "vue"; +import { cx } from "../utils.js"; + +export default defineComponent({ + name: "GnDescriptionList", + inheritAttrs: false, + props: { + items: { type: Array, default: () => [] }, + compact: { type: Boolean, default: false } + }, + setup(props, { attrs, slots }) { + return () => h("dl", { + ...attrs, + class: cx("description-list", { "description-list-compact": props.compact }, attrs.class) + }, props.items.map(item => h("div", { class: "description-list-row" }, [ + h("dt", { class: "description-list-term" }, item.term || item.label), + h("dd", { + class: cx("description-list-value", { "description-list-value-muted": item.muted }) + }, slots[item.key]?.({ item }) || item.value) + ]))); + } +}); diff --git a/src/vue/components/GnEmptyState.js b/src/vue/components/GnEmptyState.js new file mode 100644 index 0000000..afe50d9 --- /dev/null +++ b/src/vue/components/GnEmptyState.js @@ -0,0 +1,40 @@ +/** + * GnEmptyState — empty state placeholder. + * + * API-compatible with gnexus-ui-kit GnEmptyState. + * + * @typedef {Object} GnEmptyStateProps + * @property {string} title - Title text + * @property {string} [text=''] - Description text + * @property {string} [icon='essentials/package'] - VMK icon key or legacy Phosphor name + * @property {string} [variant=''] - Optional variant class suffix (error, success, ...) + * + * @slots title - Override title content + * @slots default - Override description content + * @slots actions - Action buttons area + */ + +import { defineComponent, h } from "vue"; +import { cx, iconNode } from "../utils.js"; + +export default defineComponent({ + name: "GnEmptyState", + inheritAttrs: false, + props: { + title: { type: String, required: true }, + text: { type: String, default: "" }, + icon: { type: String, default: "essentials/package" }, + variant: { type: String, default: "" } + }, + setup(props, { attrs, slots }) { + return () => h("div", { + ...attrs, + class: cx("empty-state", props.variant && `empty-state-${props.variant}`, attrs.class) + }, [ + h("div", { class: "empty-state-icon" }, [iconNode(props.icon)]), + h("h3", { class: "empty-state-title" }, slots.title?.() || props.title), + (props.text || slots.default) && h("p", { class: "empty-state-text" }, slots.default?.() || props.text), + slots.actions && h("div", { class: "empty-state-actions" }, slots.actions()) + ]); + } +}); diff --git a/src/vue/components/GnIconButton.js b/src/vue/components/GnIconButton.js new file mode 100644 index 0000000..519433b --- /dev/null +++ b/src/vue/components/GnIconButton.js @@ -0,0 +1,44 @@ +/** + * GnIconButton — icon-only button. + * + * API-compatible with gnexus-ui-kit GnIconButton. + * + * @typedef {Object} GnIconButtonProps + * @property {string} icon - VMK icon key or legacy Phosphor name with ph- prefix + * @property {string} label - Accessible label (aria-label) + * @property {string} [variant='secondary'] - primary | secondary | tertiary | ... + * @property {string} [size='md'] - xxs | xs | sm | md | lg + * @property {boolean} [disabled=false] + * @property {string} [type='button'] + */ + +import { defineComponent, h } from "vue"; +import { cx, iconNode, normalizeVariant, normalizeSize } from "../utils.js"; + +export default defineComponent({ + name: "GnIconButton", + inheritAttrs: false, + props: { + icon: { type: String, required: true }, + label: { type: String, required: true }, + variant: { type: String, default: "secondary" }, + size: { type: String, default: "md" }, + disabled: { type: Boolean, default: false }, + type: { type: String, default: "button" } + }, + setup(props, { attrs }) { + return () => h("button", { + ...attrs, + type: props.type, + disabled: props.disabled, + "aria-label": props.label, + class: cx( + "btn", + "btn-icon-only", + `btn-${normalizeVariant(props.variant)}`, + normalizeSize(props.size), + attrs.class + ) + }, [iconNode(props.icon)]); + } +}); diff --git a/src/vue/components/GnInputGroup.js b/src/vue/components/GnInputGroup.js new file mode 100644 index 0000000..616da01 --- /dev/null +++ b/src/vue/components/GnInputGroup.js @@ -0,0 +1,37 @@ +/** + * GnInputGroup — composes an input with an addon and/or action. + * + * API-compatible with gnexus-ui-kit GnInputGroup. + * + * @typedef {Object} GnInputGroupProps + * @property {boolean} [compact=false] + * @property {string} [addon=''] - Text addon (left side) + * @property {string} [icon=''] - Icon addon (left side, VMK key or legacy Phosphor name) + * + * @slots default - The input element + * @slots addon - Custom left addon content (overrides addon/icon) + * @slots action - Right-side action content (button, icon, etc.) + */ + +import { defineComponent, h } from "vue"; +import { cx, iconNode } from "../utils.js"; + +export default defineComponent({ + name: "GnInputGroup", + inheritAttrs: false, + props: { + compact: { type: Boolean, default: false }, + addon: { type: String, default: "" }, + icon: { type: String, default: "" } + }, + setup(props, { attrs, slots }) { + return () => h("div", { + ...attrs, + class: cx("input-group", { "input-group-compact": props.compact }, attrs.class) + }, [ + (props.addon || props.icon || slots.addon) && h("span", { class: "input-group-addon" }, slots.addon?.() || iconNode(props.icon) || props.addon), + slots.default?.(), + slots.action && h("span", { class: "input-group-action" }, slots.action()) + ]); + } +}); diff --git a/src/vue/components/GnPageHeader.js b/src/vue/components/GnPageHeader.js new file mode 100644 index 0000000..f9e6750 --- /dev/null +++ b/src/vue/components/GnPageHeader.js @@ -0,0 +1,50 @@ +/** + * GnPageHeader — page title area with optional subtitle and actions. + * + * API-compatible with gnexus-ui-kit GnPageHeader. + * + * @typedef {Object} GnPageHeaderProps + * @property {string} title + * @property {string} [subtitle=''] + * @property {string} [kicker=''] - Eyebrow/kicker text above the title + * @property {boolean} [compact=false] + * @property {boolean} [accent=false] + * + * @slots title - Override title content + * @slots kicker - Override kicker content + * @slots subtitle - Override subtitle content + * @slots meta - Meta row content + * @slots actions - Header actions + */ + +import { defineComponent, h } from "vue"; +import { cx } from "../utils.js"; + +export default defineComponent({ + name: "GnPageHeader", + inheritAttrs: false, + props: { + title: { type: String, required: true }, + subtitle: { type: String, default: "" }, + kicker: { type: String, default: "" }, + compact: { type: Boolean, default: false }, + accent: { type: Boolean, default: false } + }, + setup(props, { attrs, slots }) { + return () => h("header", { + ...attrs, + class: cx("page-header", { + "page-header-compact": props.compact, + "page-header-accent": props.accent + }, attrs.class) + }, [ + h("div", { class: "page-header-content" }, [ + (props.kicker || slots.kicker) && h("div", { class: "page-header-kicker" }, slots.kicker?.() || props.kicker), + h("h1", { class: "page-header-title" }, slots.title?.() || props.title), + (props.subtitle || slots.subtitle) && h("p", { class: "page-header-subtitle" }, slots.subtitle?.() || props.subtitle), + slots.meta && h("div", { class: "page-header-meta" }, slots.meta()) + ]), + slots.actions && h("div", { class: "page-header-actions" }, slots.actions()) + ]); + } +}); diff --git a/src/vue/index.js b/src/vue/index.js index c36ddf5..b7caf80 100644 --- a/src/vue/index.js +++ b/src/vue/index.js @@ -4,8 +4,10 @@ */ export { default as GnButton } from "./components/GnButton.js"; +export { default as GnIconButton } from "./components/GnIconButton.js"; export { default as VmkIcon } from "./components/Icon.js"; export { default as GnInput } from "./components/GnInput.js"; +export { default as GnInputGroup } from "./components/GnInputGroup.js"; export { default as GnBadge } from "./components/GnBadge.js"; export { default as GnAlert } from "./components/GnAlert.js"; export { default as GnCheckbox } from "./components/GnCheckbox.js"; @@ -27,8 +29,11 @@ export { default as GnTable } from "./components/GnTable.js"; export { default as GnTableRow } from "./components/GnTableRow.js"; export { default as GnTableHeader } from "./components/GnTableHeader.js"; +export { default as GnDescriptionList } from "./components/GnDescriptionList.js"; export { default as GnList } from "./components/GnList.js"; export { default as GnListItem } from "./components/GnListItem.js"; +export { default as GnEmptyState } from "./components/GnEmptyState.js"; +export { default as GnPageHeader } from "./components/GnPageHeader.js"; export { default as GnSkeleton } from "./components/GnSkeleton.js"; export { default as GnTooltip } from "./components/GnTooltip.js"; export { default as GnDropdown } from "./components/GnDropdown.js"; diff --git a/src/vue/plugin.js b/src/vue/plugin.js index a852aea..2b4ea4b 100644 --- a/src/vue/plugin.js +++ b/src/vue/plugin.js @@ -4,8 +4,10 @@ */ import GnButton from "./components/GnButton.js"; +import GnIconButton from "./components/GnIconButton.js"; import VmkIcon from "./components/Icon.js"; import GnInput from "./components/GnInput.js"; +import GnInputGroup from "./components/GnInputGroup.js"; import GnBadge from "./components/GnBadge.js"; import GnAlert from "./components/GnAlert.js"; import GnCheckbox from "./components/GnCheckbox.js"; @@ -27,8 +29,11 @@ import GnTable from "./components/GnTable.js"; import GnTableRow from "./components/GnTableRow.js"; import GnTableHeader from "./components/GnTableHeader.js"; +import GnDescriptionList from "./components/GnDescriptionList.js"; import GnList from "./components/GnList.js"; import GnListItem from "./components/GnListItem.js"; +import GnEmptyState from "./components/GnEmptyState.js"; +import GnPageHeader from "./components/GnPageHeader.js"; import GnSkeleton from "./components/GnSkeleton.js"; import GnTooltip from "./components/GnTooltip.js"; import GnDropdown from "./components/GnDropdown.js"; @@ -49,8 +54,10 @@ export const components = { GnButton, + GnIconButton, VmkIcon, GnInput, + GnInputGroup, GnBadge, GnAlert, GnCheckbox, @@ -72,8 +79,11 @@ GnTable, GnTableRow, GnTableHeader, + GnDescriptionList, GnList, GnListItem, + GnEmptyState, + GnPageHeader, GnSkeleton, GnTooltip, GnDropdown, diff --git a/tests/functional.spec.js b/tests/functional.spec.js new file mode 100644 index 0000000..9cb14e9 --- /dev/null +++ b/tests/functional.spec.js @@ -0,0 +1,108 @@ +const { test, expect } = require("@playwright/test"); + +test.describe("@functional", () => { + test.beforeEach(async ({ page }) => { + await page.goto("/"); + // Ensure the page has no console errors or uncaught exceptions during interactions. + page.on("pageerror", error => { + throw new Error(`Uncaught exception: ${error.message}`); + }); + page.on("console", message => { + if (message.type() === "error") { + throw new Error(`Console error: ${message.text()}`); + } + }); + }); + + test("accordion toggles open and closed", async ({ page }) => { + const summary = page.locator(".accordion-summary", { hasText: "Account details" }); + const details = summary.locator(".."); + await expect(details).not.toHaveAttribute("open", ""); + await summary.click(); + await expect(details).toHaveAttribute("open", ""); + await expect(details).not.toHaveAttribute("data-animating", "true"); + await summary.click(); + await expect(details).not.toHaveAttribute("data-animating", "true"); + await expect(details).not.toHaveAttribute("open", ""); + }); + + test("tabs switch panels", async ({ page }) => { + const tabs = page.locator(".tabs").first(); + const detailsTab = tabs.locator(".tab", { hasText: "Details" }); + const detailsPanel = page.locator("#tab-panel-details"); + await expect(detailsPanel).toHaveAttribute("hidden", ""); + await detailsTab.click(); + await expect(detailsTab).toHaveClass(/tab-active/); + await expect(detailsPanel).not.toHaveAttribute("hidden", ""); + await expect(detailsPanel).toBeVisible(); + }); + + test("dropdown opens and closes", async ({ page }) => { + const trigger = page.locator("[data-dropdown-trigger]", { hasText: "Open dropdown" }); + const menu = page.locator(".dropdown-menu").first(); + await trigger.click(); + await expect(menu).toHaveClass(/is-open/); + await page.locator("body").click(); + await expect(menu).not.toHaveClass(/is-open/); + }); + + test("tooltip shows on hover", async ({ page }) => { + const trigger = page.locator(".tooltip-trigger", { hasText: "Top" }); + const wrapper = trigger.locator("xpath=.."); + await trigger.hover(); + await expect(wrapper).toHaveClass(/is-visible/); + await page.mouse.move(0, 0); + await expect(wrapper).not.toHaveClass(/is-visible/); + }); + + test("popover toggles and closes on outside click", async ({ page }) => { + const trigger = page.locator("[data-popover-toggle]"); + const popover = page.locator(".popover").first(); + await trigger.click(); + await expect(popover).toHaveClass(/is-open/); + await page.locator("body").click(); + await expect(popover).not.toHaveClass(/is-open/); + }); + + test("navigation shell toggles sidebar", async ({ page }) => { + await page.setViewportSize({ width: 800, height: 600 }); + const shell = page.locator(".navigation-shell").first(); + const toggle = shell.locator("[data-navigation-toggle]"); + const sidebar = shell.locator(".navigation-sidebar"); + const backdrop = shell.locator(".navigation-backdrop"); + await toggle.click(); + await expect(sidebar).toHaveClass(/is-open/); + await expect(backdrop).toHaveClass(/is-visible/); + await backdrop.click(); + await expect(sidebar).not.toHaveClass(/is-open/); + await expect(backdrop).not.toHaveClass(/is-visible/); + }); + + test("modal opens and closes", async ({ page }) => { + const trigger = page.getByRole("button", { name: "Open modal" }); + const modal = page.locator("#demo-modal"); + await trigger.click(); + await expect(modal).toHaveClass(/a-show/); + await modal.locator(".modal-close").click(); + await expect(modal).not.toHaveClass(/a-show/); + }); + + test("drawer opens and closes", async ({ page }) => { + const trigger = page.getByRole("button", { name: "Open drawer" }); + const drawer = page.locator("#demo-drawer"); + await trigger.click(); + await expect(drawer).toHaveClass(/a-show/); + await drawer.locator(".drawer-backdrop").click(); + await expect(drawer).not.toHaveClass(/a-show/); + }); + + test("toast appears and can be closed", async ({ page }) => { + const trigger = page.getByRole("button", { name: "Info toast" }); + await trigger.click(); + const toast = page.locator(".toast.toast-info").first(); + await expect(toast).toBeVisible(); + await expect(toast).toContainText("This is an info toast."); + await toast.locator(".toast-close").click(); + await expect(toast).not.toBeVisible(); + }); +});