diff --git a/package.json b/package.json index 029d109f..19f5fc52 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "scripts": { "preinstall": "npx only-allow pnpm", - "dev": "next dev --turbopack", + "dev": "next dev", "build": "next build", "start": "next start -p 1030", "stage": "next start -p 1031", @@ -25,7 +25,7 @@ "dependencies": { "@ducanh2912/next-pwa": "^10.2.9", "@ericblade/quagga2": "^1.8.4", - "@frontendnetwork/veganify": "^1.2.9", + "@frontendnetwork/veganify": "^1.3.0", "@types/node": "22.8.1", "@types/react-dom": "npm:types-react-dom@19.0.0-rc.1", "jest-worker": "^29.7.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index eb331358..b017dde5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -30,8 +30,8 @@ importers: specifier: ^1.8.4 version: 1.8.4 '@frontendnetwork/veganify': - specifier: ^1.2.9 - version: 1.2.9 + specifier: ^1.3.0 + version: 1.3.0 '@types/node': specifier: 22.8.1 version: 22.8.1 @@ -819,8 +819,8 @@ packages: '@formatjs/intl-localematcher@0.5.6': resolution: {integrity: sha512-roz1+Ba5e23AHX6KUAWmLEyTRZegM5YDuxuvkHCyK3RJddf/UXB2f+s7pOMm9ktfPGla0g+mQXOn5vsuYirnaA==} - '@frontendnetwork/veganify@1.2.9': - resolution: {integrity: sha512-npn7Y51JW9c2IakO4X9BAByZoSm9NhJXDNE3W0+kJR7G7KBmOr1R+8xs7QB4wQ/cTeuS0SHY90QbWubnCgoawg==} + '@frontendnetwork/veganify@1.3.0': + resolution: {integrity: sha512-0vikV+EnScvrI7/Gi1cpJynomiSGz2t7HAWcR0tLUhhdlBmDimAvIxgWZYxPl2hEYjI0J5PSHNxltV/nePndAQ==} '@humanfs/core@0.19.0': resolution: {integrity: sha512-2cbWIHbZVEweE853g8jymffCA+NCMiuqeECeBBLm8dg2oFdjuGJhgN4UAbI+6v0CKbbhvtXA4qV8YR5Ji86nmw==} @@ -5068,7 +5068,7 @@ snapshots: dependencies: tslib: 2.6.2 - '@frontendnetwork/veganify@1.2.9': {} + '@frontendnetwork/veganify@1.3.0': {} '@humanfs/core@0.19.0': {} diff --git a/src/components/IngredientsCheck/IngredientsForm.tsx b/src/components/IngredientsCheck/IngredientsForm.tsx index 889f20b4..e5d507d2 100644 --- a/src/components/IngredientsCheck/IngredientsForm.tsx +++ b/src/components/IngredientsCheck/IngredientsForm.tsx @@ -14,14 +14,21 @@ export function IngredientsForm() { vegan: null, surelyVegan: [], notVegan: [], - maybeVegan: [], + maybeNotVegan: [], + unknown: [], }); const [error, setError] = useState(null); const [loading, setLoading] = useState(false); async function handleSubmit(event: FormEvent) { event.preventDefault(); - setResult({ vegan: null, surelyVegan: [], notVegan: [], maybeVegan: [] }); + setResult({ + vegan: null, + surelyVegan: [], + notVegan: [], + maybeNotVegan: [], + unknown: [], + }); setError(null); const formData = new FormData(event.currentTarget); diff --git a/src/components/IngredientsCheck/IngredientsList.tsx b/src/components/IngredientsCheck/IngredientsList.tsx index 4be5eb71..a630ed22 100644 --- a/src/components/IngredientsCheck/IngredientsList.tsx +++ b/src/components/IngredientsCheck/IngredientsList.tsx @@ -1,23 +1,41 @@ -import React from "react"; +import { IconClassType } from "./models/Tooltip"; +import { TranslationFunction } from "./models/TranslateFunction"; +import { TooltipClient } from "./shared/Tooltip"; -export function IngredientList({ - items, - iconClass, -}: { +interface IngredientListProps { items: string[]; - iconClass: string; -}) { + iconClass: IconClassType; + t: TranslationFunction; +} + +export function IngredientList({ items, iconClass, t }: IngredientListProps) { + const tooltipMessages = { + "maybe-vegan": t("maybe_vegan"), + "unknown-vegan": t("unknown_vegan"), + } as const; + + const shouldShowTooltip = + iconClass.includes("maybe-vegan") || iconClass.includes("unknown-vegan"); + const tooltipBaseClass = iconClass.split( + " " + )[0] as keyof typeof tooltipMessages; + const tooltipMessage = shouldShowTooltip + ? tooltipMessages[tooltipBaseClass] + : ""; + return ( <> {items.map((item) => ( -
-
- {item.charAt(0).toUpperCase() + item.slice(1)} -
-
- + +
+
+ {item.charAt(0).toUpperCase() + item.slice(1)} +
+
+ +
-
+ ))} ); diff --git a/src/components/IngredientsCheck/ResultsDisplay.tsx b/src/components/IngredientsCheck/ResultsDisplay.tsx index c4138f53..90188ee6 100644 --- a/src/components/IngredientsCheck/ResultsDisplay.tsx +++ b/src/components/IngredientsCheck/ResultsDisplay.tsx @@ -1,14 +1,14 @@ import { IngredientList } from "./IngredientsList"; import { IngredientResult } from "./models/IngredientResult"; +import { TranslationFunction } from "./models/TranslateFunction"; import { SourceInfo } from "./SourceInfo"; -export function ResultDisplay({ - result, - t, -}: { +interface ResultDisplayProps { result: IngredientResult; - t: (key: string, values?: Record) => string; -}) { + t: TranslationFunction; +} + +export function ResultDisplay({ result, t }: ResultDisplayProps) { return (
@@ -28,14 +28,22 @@ export function ResultDisplay({ +
diff --git a/src/components/IngredientsCheck/models/IngredientResult.ts b/src/components/IngredientsCheck/models/IngredientResult.ts index 0d469710..f8ff293f 100644 --- a/src/components/IngredientsCheck/models/IngredientResult.ts +++ b/src/components/IngredientsCheck/models/IngredientResult.ts @@ -2,5 +2,6 @@ export interface IngredientResult { vegan: boolean | null; surelyVegan: string[]; notVegan: string[]; - maybeVegan: string[]; + maybeNotVegan: string[]; + unknown: string[]; } diff --git a/src/components/IngredientsCheck/models/Tooltip.ts b/src/components/IngredientsCheck/models/Tooltip.ts new file mode 100644 index 00000000..1a3fb564 --- /dev/null +++ b/src/components/IngredientsCheck/models/Tooltip.ts @@ -0,0 +1,10 @@ +export type IconClassType = + | "vegan icon-ok" + | "non-vegan icon-cancel" + | "unknown-vegan icon-help" + | "maybe-vegan icon-attention-alt"; + +export interface TooltipMessages { + "maybe-vegan": string; + "unkown-vegan": string; +} diff --git a/src/components/IngredientsCheck/models/TranslateFunction.ts b/src/components/IngredientsCheck/models/TranslateFunction.ts new file mode 100644 index 00000000..4edd03ab --- /dev/null +++ b/src/components/IngredientsCheck/models/TranslateFunction.ts @@ -0,0 +1,4 @@ +export type TranslationFunction = ( + key: string, + values?: Record +) => string; diff --git a/src/components/IngredientsCheck/shared/Tooltip.tsx b/src/components/IngredientsCheck/shared/Tooltip.tsx new file mode 100644 index 00000000..076073ce --- /dev/null +++ b/src/components/IngredientsCheck/shared/Tooltip.tsx @@ -0,0 +1,32 @@ +"use client"; + +import { useState } from "react"; + +interface TooltipProps { + message: string; + children: React.ReactNode; +} + +export function TooltipClient({ message, children }: TooltipProps) { + const [isVisible, setIsVisible] = useState(false); + + if (!message) { + return children; + } + + return ( +
setIsVisible(true)} + onMouseLeave={() => setIsVisible(false)} + > + {children} + {isVisible && ( +
+ {message} +
+
+ )} +
+ ); +} diff --git a/src/components/IngredientsCheck/utils/actions.test.ts b/src/components/IngredientsCheck/utils/actions.test.ts index 5448d285..35207eb7 100644 --- a/src/components/IngredientsCheck/utils/actions.test.ts +++ b/src/components/IngredientsCheck/utils/actions.test.ts @@ -1,136 +1,126 @@ import Veganify from "@frontendnetwork/veganify"; +import { checkIngredients } from "./actions"; + jest.mock("@frontendnetwork/veganify", () => ({ - getProductByBarcode: jest.fn(), + __esModule: true, + default: { + checkIngredientsListV1: jest.fn(), + }, })); -async function testFetchProduct(barcode: string) { - try { - const data = await Veganify.getProductByBarcode( - barcode, - process.env.NEXT_PUBLIC_STAGING === "true" - ); - if (data && "product" in data && "sources" in data) { - return { - product: data.product, - sources: data.sources, - status: data.status, - }; - } else if (data && "status" in data) { - return { - status: data.status, - }; - } - throw new Error("Invalid response format"); - } catch (error) { - console.error("Product fetch error:", error); - throw error; - } -} - -describe("fetchProduct", () => { +describe("checkIngredients", () => { + // Reset all mocks before each test beforeEach(() => { jest.clearAllMocks(); - // Reset environment variables - process.env.NEXT_PUBLIC_STAGING = undefined; }); - it("returns product and sources when available", async () => { - const mockResponse = { - product: { - name: "Test Product", + it("should successfully check ingredients and return formatted data", async () => { + // Mock successful API response + const mockApiResponse = { + data: { vegan: true, + surely_vegan: ["apple", "banana"], + not_vegan: [], + maybe_not_vegan: [], + unknown: ["artificial-flavor"], }, - sources: { - openFoodFacts: true, - }, - status: 200, }; - (Veganify.getProductByBarcode as jest.Mock).mockResolvedValueOnce( - mockResponse + (Veganify.checkIngredientsListV1 as jest.Mock).mockResolvedValue( + mockApiResponse ); - const result = await testFetchProduct("4000417025005"); + const result = await checkIngredients("apple, banana, artificial-flavor"); - expect(result).toEqual(mockResponse); - expect(Veganify.getProductByBarcode).toHaveBeenCalledWith( - "4000417025005", - false + // Check if the function returns the expected formatted data + expect(result).toEqual({ + vegan: true, + surelyVegan: ["apple", "banana"], + notVegan: [], + maybeNotVegan: [], + unknown: ["artificial-flavor"], + }); + + // Verify Veganify was called with correct parameters + expect(Veganify.checkIngredientsListV1).toHaveBeenCalledWith( + "apple, banana, artificial-flavor", + process.env.NEXT_PUBLIC_STAGING === "true" ); }); - it("returns only status when no product found", async () => { - const mockResponse = { - status: 404, - }; - - (Veganify.getProductByBarcode as jest.Mock).mockResolvedValueOnce( - mockResponse + it("should throw an error when ingredients string is empty", async () => { + await expect(checkIngredients("")).rejects.toThrow( + "Ingredients cannot be empty" + ); + await expect(checkIngredients(" ")).rejects.toThrow( + "Ingredients cannot be empty" ); - const result = await testFetchProduct("4000417025005"); - - expect(result).toEqual({ status: 404 }); + // Verify Veganify was not called + expect(Veganify.checkIngredientsListV1).not.toHaveBeenCalled(); }); - it("throws error when API call fails", async () => { - const mockError = new Error("API Error"); - (Veganify.getProductByBarcode as jest.Mock).mockRejectedValueOnce( - mockError + it("should throw an error when API call fails", async () => { + // Mock API failure + (Veganify.checkIngredientsListV1 as jest.Mock).mockRejectedValue( + new Error("API Error") ); - await expect(testFetchProduct("4000417025005")).rejects.toThrow( - "API Error" + await expect(checkIngredients("apple")).rejects.toThrow( + "Failed to check ingredients" + ); + + // Verify Veganify was called + expect(Veganify.checkIngredientsListV1).toHaveBeenCalledWith( + "apple", + process.env.NEXT_PUBLIC_STAGING === "true" ); }); - it("uses staging flag correctly", async () => { - const mockResponse = { - product: { - name: "Test Product", - vegan: true, + it("should handle non-vegan ingredients correctly", async () => { + // Mock API response with non-vegan ingredients + const mockApiResponse = { + data: { + vegan: false, + surely_vegan: ["apple"], + not_vegan: ["gelatin"], + maybe_not_vegan: ["sugar"], + unknown: [], }, - sources: { - openFoodFacts: true, - }, - status: 200, }; - (Veganify.getProductByBarcode as jest.Mock).mockResolvedValue(mockResponse); + (Veganify.checkIngredientsListV1 as jest.Mock).mockResolvedValue( + mockApiResponse + ); + + const result = await checkIngredients("apple, gelatin, sugar"); + + expect(result).toEqual({ + vegan: false, + surelyVegan: ["apple"], + notVegan: ["gelatin"], + maybeNotVegan: ["sugar"], + unknown: [], + }); + }); + + it("should handle staging environment flag correctly", async () => { + const originalEnv = process.env.NEXT_PUBLIC_STAGING; // Test with staging true process.env.NEXT_PUBLIC_STAGING = "true"; - await testFetchProduct("4000417025005"); - expect(Veganify.getProductByBarcode).toHaveBeenLastCalledWith( - "4000417025005", - true - ); + await checkIngredients("apple"); + expect(Veganify.checkIngredientsListV1).toHaveBeenCalledWith("apple", true); // Test with staging false process.env.NEXT_PUBLIC_STAGING = "false"; - await testFetchProduct("4000417025005"); - expect(Veganify.getProductByBarcode).toHaveBeenLastCalledWith( - "4000417025005", + await checkIngredients("apple"); + expect(Veganify.checkIngredientsListV1).toHaveBeenCalledWith( + "apple", false ); - // Test with staging undefined - process.env.NEXT_PUBLIC_STAGING = undefined; - await testFetchProduct("4000417025005"); - expect(Veganify.getProductByBarcode).toHaveBeenLastCalledWith( - "4000417025005", - false - ); - }); - - it("handles invalid response format", async () => { - (Veganify.getProductByBarcode as jest.Mock).mockResolvedValueOnce({ - invalidField: "invalid", - }); - - await expect(testFetchProduct("4000417025005")).rejects.toThrow( - "Invalid response format" - ); + process.env.NEXT_PUBLIC_STAGING = originalEnv; }); }); diff --git a/src/components/IngredientsCheck/utils/actions.ts b/src/components/IngredientsCheck/utils/actions.ts index d70f0712..1af90c0b 100644 --- a/src/components/IngredientsCheck/utils/actions.ts +++ b/src/components/IngredientsCheck/utils/actions.ts @@ -9,7 +9,7 @@ export async function checkIngredients(ingredients: string) { } try { - const data = await Veganify.checkIngredientsList( + const data = await Veganify.checkIngredientsListV1( ingredients, process.env.NEXT_PUBLIC_STAGING === "true" ); @@ -18,7 +18,8 @@ export async function checkIngredients(ingredients: string) { vegan: data.data.vegan, surelyVegan: data.data.surely_vegan, notVegan: data.data.not_vegan, - maybeVegan: data.data.maybe_vegan, + maybeNotVegan: data.data.maybe_not_vegan, + unknown: data.data.unknown, }; } catch (error) { console.error(error); diff --git a/src/locales/cz.json b/src/locales/cz.json index 5f262e64..fdb44554 100644 --- a/src/locales/cz.json +++ b/src/locales/cz.json @@ -98,6 +98,8 @@ "cannotbeempty": "Seznam ingrediencí nesmí být prázdný ani obsahovat speciální znaky a musí být oddělen čárkou!", "licenses": "Licence", "licenses_desc": "Veganify používá ke shromažďování informací o výrobku různé databáze a rozhraní API. Informace jsou licencovány pod následujícími licencemi:", - "languagewarning": "Tyto výsledky mohou být nesprávné z důvodu chyb v překladu nebo chybějících informací. Výsledky jsou přeloženy pomocí {deepl}." + "languagewarning": "Tyto výsledky mohou být nesprávné z důvodu chyb v překladu nebo chybějících informací. Výsledky jsou přeloženy pomocí {deepl}.", + "maybe_vegan": "Tato složka může obsahovat živočišné produkty.", + "unknown_vegan": "O této složce nemáme žádné informace." } } diff --git a/src/locales/de.json b/src/locales/de.json index 1f64223a..2ef845f7 100644 --- a/src/locales/de.json +++ b/src/locales/de.json @@ -98,7 +98,9 @@ "source": "Datenquelle", "cannotbeempty": "Die Zutatenliste darf nicht leer sein oder Sonderzeichen enthalten und muss durch Kommata getrennt sein!", "licenses": "Lizenzen", - "licenses_desc":"Veganify benutzt verschiedene Datenquellen, um Informationen über ein Produkt zu sammeln. Diese Informationen stehen unter den nachfolgenden Lizenzen:", - "languagewarning": "Diese Ergebnisse können aufgrund von Übersetzungsfehlern oder fehlenden Informationen inkorrekt sein. Die Ergebnisse werden mit {deepl} übersetzt." + "licenses_desc": "Veganify benutzt verschiedene Datenquellen, um Informationen über ein Produkt zu sammeln. Diese Informationen stehen unter den nachfolgenden Lizenzen:", + "languagewarning": "Diese Ergebnisse können aufgrund von Übersetzungsfehlern oder fehlenden Informationen inkorrekt sein. Die Ergebnisse werden mit {deepl} übersetzt.", + "maybe_vegan": "Diese Zutat könnte tierische Produkte enthalten.", + "unknown_vegan": "Wir haben keine Informationen über diese Zutat." } } diff --git a/src/locales/en.json b/src/locales/en.json index 465390b8..c784bc10 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -98,6 +98,8 @@ "cannotbeempty": "Ingredients-list cannot be empty or include special characters and has to be comma seperated!", "licenses": "Licenses", "licenses_desc": "Veganify uses different databases and APIs to gather information about a product. The information is licensed under the following licenses:", - "languagewarning": "These results may be incorrect due to translation errors or missing information. The results are translated with {deepl}." + "languagewarning": "These results may be incorrect due to translation errors or missing information. The results are translated with {deepl}.", + "maybe_vegan": "This ingredient could contain animal products.", + "unknown_vegan": "We don't have information about this ingredient." } } diff --git a/src/locales/es.json b/src/locales/es.json index cff042c3..d207ca39 100644 --- a/src/locales/es.json +++ b/src/locales/es.json @@ -98,6 +98,8 @@ "cannotbeempty": "La lista de ingredientes no puede estar vacía ni incluir caracteres especiales y tiene que estar separada por comas.", "licenses": "Licencias", "licenses_desc": "Veganify utiliza varias fuentes de datos para recoger informaci\u00F3n sobre un producto. Esta informaci\u00F3n est\u00E1 bajo las siguientes licencias:", - "languagewarning": "Estos resultados pueden ser incorrectos debido a errores de traducción o falta de información. Los resultados se traducen con {deepl}." + "languagewarning": "Estos resultados pueden ser incorrectos debido a errores de traducción o falta de información. Los resultados se traducen con {deepl}.", + "maybe_vegan": "Este ingrediente podría contener productos de origen animal.", + "unknown_vegan": "No tenemos información sobre este ingrediente." } } diff --git a/src/locales/fr.json b/src/locales/fr.json index a8dbfa68..a9063037 100644 --- a/src/locales/fr.json +++ b/src/locales/fr.json @@ -98,6 +98,8 @@ "cannotbeempty": "La liste des ingr\u00e9dients ne peut \u00eatre vide ou inclure des caract\u00e8res sp\u00e9ciaux et doit \u00eatre s\u00e9par\u00e9e par des virgules !", "licenses": "Licenses", "licenses_desc": "Veganify utilise diff\u00E9rentes sources de donn\u00E9es pour collecter des informations sur un produit. Ces informations sont soumises aux licences suivantes :", - "languagewarning": "Ces résultats peuvent être incorrects en raison d'erreurs de traduction ou d'informations manquantes. Les résultats sont traduits avec {deepl}." + "languagewarning": "Ces résultats peuvent être incorrects en raison d'erreurs de traduction ou d'informations manquantes. Les résultats sont traduits avec {deepl}.", + "maybe_vegan": "Cet ingrédient pourrait contenir des produits d'origine animale.", + "unknown_vegan": "Nous ne disposons pas d'informations sur cet ingrédient." } } diff --git a/src/locales/pl.json b/src/locales/pl.json index 915eae42..a274e76b 100644 --- a/src/locales/pl.json +++ b/src/locales/pl.json @@ -98,6 +98,8 @@ "cannotbeempty": "Lista składników nie może być pusta, zawierać znaków specjalnych i musi być oddzielona przecinkiem!", "licenses": "Licencje", "licenses_desc": "Veganify uses different databases and APIs to gather information about a product. The information is licensed under the following licenses:", - "languagewarning": "Wyniki mogą być nieprawidłowe z racji błędów w tłumaczeniach lub niekompletnych informacji. Wyniki są tłumaczone przez {deepl}." + "languagewarning": "Wyniki mogą być nieprawidłowe z racji błędów w tłumaczeniach lub niekompletnych informacji. Wyniki są tłumaczone przez {deepl}.", + "maybe_vegan": "Ten składnik może zawierać produkty pochodzenia zwierzęcego.", + "unknown_vegan": "Nie mamy informacji o tym składniku." } } diff --git a/src/styles/_globals/_mixins.scss b/src/styles/_globals/_mixins.scss index 5950cfaf..48f78fe5 100644 --- a/src/styles/_globals/_mixins.scss +++ b/src/styles/_globals/_mixins.scss @@ -3,8 +3,8 @@ // converts several units to em (supports px, pt, pc, in, mm, cm) @function em($size) { - @if not unitless($size) { - @if unit($size)==em { + @if not math.is-unitless($size) { + @if math.unit($size)==em { @return $size; } diff --git a/src/styles/_globals/_roots.scss b/src/styles/_globals/_roots.scss index e7f0ae0e..ee0d5950 100644 --- a/src/styles/_globals/_roots.scss +++ b/src/styles/_globals/_roots.scss @@ -12,6 +12,7 @@ --body-h1-fontsize: 2.0833rem; --body-h2-fontsize: 1.5rem; --non-vegan: #ee5253; + --maybe-vegan: #ff9d00; --vegan: #10ac84; --unknown: #576574; --link-color: #ccc; @@ -82,4 +83,4 @@ html[data-theme="oled"] { --modal-fg: #fff; --modal-full-btn: #3e3e3e; --modal-border: 0.1rem #3e3e3e solid; -} +} \ No newline at end of file diff --git a/src/styles/_globals/_vegancheckicons.scss b/src/styles/_globals/_vegancheckicons.scss index e529b9d0..16029912 100644 --- a/src/styles/_globals/_vegancheckicons.scss +++ b/src/styles/_globals/_vegancheckicons.scss @@ -1,8 +1,7 @@ @font-face { font-family: "vegancheckicons"; src: url("../font/vegancheckicons.eot?33525214"); - src: url("../font/vegancheckicons.eot?33525214#iefix") - format("embedded-opentype"), + src: url("../font/vegancheckicons.eot?33525214#iefix") format("embedded-opentype"), url("../font/vegancheckicons.woff2?33525214") format("woff2"), url("../font/vegancheckicons.woff?33525214") format("woff"), url("../font/vegancheckicons.ttf?33525214") format("truetype"), @@ -15,8 +14,7 @@ @font-face { font-family: "additional_icons"; src: url("../font/additional_icons.eot?64205737"); - src: url("../font/additional_icons.eot?64205737#iefix") - format("embedded-opentype"), + src: url("../font/additional_icons.eot?64205737#iefix") format("embedded-opentype"), url("../font/additional_icons.svg?64205737#additional_icons") format("svg"); font-weight: normal; font-style: normal; @@ -26,8 +24,7 @@ @font-face { font-family: "nutriscore"; src: url("../font/nutriscore.eot?4788995906"); - src: url("../font/nutriscore.eot?4788995906#iefix") - format("embedded-opentype"), + src: url("../font/nutriscore.eot?4788995906#iefix") format("embedded-opentype"), url("../font/nutriscore.woff2?4788995906") format("woff2"), url("../font/nutriscore.woff?4788995906") format("woff"), url("../font/nutriscore.ttf?4788995906") format("truetype"), @@ -178,6 +175,11 @@ content: "\e804"; } +/* '' */ +.icon-attention-alt:before { + content: '\f12a'; +} + /* '' */ .icon-barcode:before { content: "\e805"; @@ -292,4 +294,4 @@ .icon-kofi:before { content: "\f097"; margin-right: 0.7rem; -} +} \ No newline at end of file diff --git a/src/styles/_modules/banner.scss b/src/styles/_modules/_banner.scss similarity index 65% rename from src/styles/_modules/banner.scss rename to src/styles/_modules/_banner.scss index 657b9392..a2207cdd 100644 --- a/src/styles/_modules/banner.scss +++ b/src/styles/_modules/_banner.scss @@ -1,3 +1,5 @@ +@use "../_globals/mixins"; + /* Open Shortcuts */ #shortcut { cursor: pointer; @@ -13,27 +15,27 @@ width: 100%; border-bottom: var(--pwa-border); text-align: left; - height: rem(44.8px); + height: mixins.rem(44.8px); .flex-container { display: flex; flex-flow: row wrap; writing-mode: horizontal-tb; padding: 0; - padding-left: rem(16px); - padding-right: rem(16px); + padding-left: mixins.rem(16px); + padding-right: mixins.rem(16px); margin: 0; list-style: none; } .flex-item { - height: rem(40px); + height: mixins.rem(40px); margin: 0; padding: 0; - padding-top: rem(3.2px); + padding-top: mixins.rem(3.2px); &:first-child { - width: rem(40px); + width: mixins.rem(40px); max-width: 20%; } @@ -42,7 +44,7 @@ } &:last-child { - margin-top: rem(6.4px); + margin-top: mixins.rem(6.4px); margin-left: auto; float: right; max-width: 35%; @@ -51,18 +53,18 @@ } .heading { - font-size: rem(12.8px); + font-size: mixins.rem(12.8px); font-family: SF Pro Text, system-ui, -apple-system, BlinkMacSystemFont, Helvetica Neue, Helvetica, Arial, sans-serif; color: #fff; - @include theme(light) { + @include mixins.theme(light) { color: #fff; } } .subheading { - font-size: rem(11.2px); + font-size: mixins.rem(11.2px); font-family: SF Pro Text, system-ui, -apple-system, BlinkMacSystemFont, Helvetica Neue, Helvetica, Arial, sans-serif; display: block; @@ -72,7 +74,7 @@ overflow: hidden; white-space: nowrap; - @include theme(light) { + @include mixins.theme(light) { color: #c0c5d5; } } @@ -82,23 +84,23 @@ } .button { - font-size: rem(11.2px); + font-size: mixins.rem(11.2px); color: #fff; text-transform: uppercase; background: #007aff; - padding: rem(8px); - padding-left: rem(24px); - padding-right: rem(24px); - border-radius: rem(48px); + padding: mixins.rem(8px); + padding-left: mixins.rem(24px); + padding-right: mixins.rem(24px); + border-radius: mixins.rem(48px); - @include theme(light) { + @include mixins.theme(light) { background: #fff; color: #7f8fa6; } } img { - width: rem(32px); + width: mixins.rem(32px); } } @@ -116,10 +118,10 @@ overflow: hidden; border-bottom: var(--pwa-border); text-align: left; - height: rem(64px); + height: mixins.rem(64px); display: none; - @include mq(s-phone) { + @include mixins.mq(s-phone) { height: 3.5rem; } @@ -128,38 +130,38 @@ flex-flow: row wrap; writing-mode: horizontal-tb; padding: 0; - padding-left: rem(16px); - padding-right: rem(16px); + padding-left: mixins.rem(16px); + padding-right: mixins.rem(16px); margin: 0; list-style: none; - @include mq(s-phone) { + @include mixins.mq(s-phone) { padding-left: 0.5rem; padding-right: 0.5rem; } } .flex-item { - height: rem(40px); + height: mixins.rem(40px); margin: 0; padding: 0; - padding-top: rem(4.8px); + padding-top: mixins.rem(4.8px); &:first-child { - font-size: rem(24px); - padding-top: rem(16px); - padding-right: rem(8px); + font-size: mixins.rem(24px); + padding-top: mixins.rem(16px); + padding-right: mixins.rem(8px); font-weight: 100; color: #ccc; opacity: 0.2; } &:nth-child(3) { - padding-top: rem(11.2px); + padding-top: mixins.rem(11.2px); } &:last-child { - margin-top: rem(12px); + margin-top: mixins.rem(12px); margin-left: auto; float: right; justify-content: flex-end; @@ -167,34 +169,34 @@ } .heading { - font-size: rem(12.8px); + font-size: mixins.rem(12.8px); font-family: SF Pro Text, system-ui, -apple-system, BlinkMacSystemFont, Helvetica Neue, Helvetica, Arial, sans-serif; color: #fff; font-weight: bold; - @include mq(s-phone) { + @include mixins.mq(s-phone) { font-size: 0.65rem; } - @include theme(light) { + @include mixins.theme(light) { color: #fff; } } .subheading { - font-size: rem(11.2px); + font-size: mixins.rem(11.2px); font-family: SF Pro Text, system-ui, -apple-system, BlinkMacSystemFont, Helvetica Neue, Helvetica, Arial, sans-serif; display: block; color: #ccc; font-weight: 400; - @include mq(s-phone) { + @include mixins.mq(s-phone) { font-size: 0.55rem; } - @include theme(light) { + @include mixins.theme(light) { color: #c0c5d5; } } @@ -204,34 +206,34 @@ } .button { - font-size: rem(11.2px); + font-size: mixins.rem(11.2px); font-weight: bold; color: #fff; text-transform: uppercase; background: #007aff; - padding: rem(8px); - padding-left: rem(24px); - padding-right: rem(24px); - border-radius: rem(48px); + padding: mixins.rem(8px); + padding-left: mixins.rem(24px); + padding-right: mixins.rem(24px); + border-radius: mixins.rem(48px); - @include mq(s-phone) { + @include mixins.mq(s-phone) { font-size: 0.5rem; } - @include theme(light) { + @include mixins.theme(light) { background: #fff; color: #7f8fa6; } } img { - width: rem(51.2px); - border: rem(0.8px) grey solid; - border-radius: rem(12px); - margin-right: rem(8px); + width: mixins.rem(51.2px); + border: mixins.rem(0.8px) grey solid; + border-radius: mixins.rem(12px); + margin-right: mixins.rem(8px); - @include mq(s-phone) { + @include mixins.mq(s-phone) { width: 2.8rem; } } -} +} \ No newline at end of file diff --git a/src/styles/_modules/buttons.scss b/src/styles/_modules/_buttons.scss similarity index 64% rename from src/styles/_modules/buttons.scss rename to src/styles/_modules/_buttons.scss index 2873adc1..96f240c9 100644 --- a/src/styles/_modules/buttons.scss +++ b/src/styles/_modules/_buttons.scss @@ -1,3 +1,5 @@ +@use "../_globals/mixins"; + /* Buttons */ button { width: 10%; @@ -5,21 +7,21 @@ button { cursor: pointer; border-top-left-radius: 0; border-bottom-left-radius: 0; - padding: rem(16px); + padding: mixins.rem(16px); text-align: center; color: #000; font-weight: var(--body-fontweight); transition: 0.5s; - border: rem(0.16px) solid #ccc; + border: mixins.rem(0.16px) solid #ccc; height: 4rem; - @include mq(s-desktop) { + @include mixins.mq(s-desktop) { width: 15%; padding-left: 0; padding-right: 0; } - @include mq(phone) { + @include mixins.mq(phone) { width: 20%; padding-left: 0; padding-right: 0; @@ -32,23 +34,23 @@ button:focus { } .btn-dark { - margin-top: rem(12.8px) !important; + margin-top: mixins.rem(12.8px) !important; display: inline-block; min-width: auto; font-weight: 400; line-height: 1.5; text-align: center; text-decoration: none; - border: rem(1.008px) solid #424242; + border: mixins.rem(1.008px) solid #424242; cursor: pointer; - padding: rem(4px) rem(8px); - font-size: rem(14px); - border-radius: rem(3.2px); + padding: mixins.rem(4px) mixins.rem(8px); + font-size: mixins.rem(14px); + border-radius: mixins.rem(3.2px); color: #fff !important; background-color: #212529; transition: 0.5s; - @include mq(s-phone) { + @include mixins.mq(s-phone) { width: 6.5rem; font-size: 0.9rem; } @@ -56,7 +58,7 @@ button:focus { &:hover, &:focus { color: #fff; - border: rem(1.008px) solid #6b6b6b; + border: mixins.rem(1.008px) solid #6b6b6b; } &:active { @@ -66,9 +68,9 @@ button:focus { } #tweet { - margin-right: rem(3.2px); + margin-right: mixins.rem(3.2px); } .nolink { text-decoration: none; -} +} \ No newline at end of file diff --git a/src/styles/_modules/form.scss b/src/styles/_modules/_form.scss similarity index 60% rename from src/styles/_modules/form.scss rename to src/styles/_modules/_form.scss index a8cdd697..4e7686f3 100644 --- a/src/styles/_modules/form.scss +++ b/src/styles/_modules/_form.scss @@ -1,26 +1,28 @@ +@use "../_globals/mixins"; + /* Main container with border */ .form { word-wrap: break-word; background: #fff; - border: rem(2px) solid #f5f5f5; - border-radius: rem(10px); + border: mixins.rem(2px) solid #f5f5f5; + border-radius: mixins.rem(10px); margin: 0 auto; - margin-bottom: rem(24px); - padding: rem(64px); - line-height: rem(30px); - box-shadow: 0 rem(25px) rem(50px) rem(-12px) rgba(0, 0, 0, 0.25); + margin-bottom: mixins.rem(24px); + padding: mixins.rem(64px); + line-height: mixins.rem(30px); + box-shadow: 0 mixins.rem(25px) mixins.rem(50px) mixins.rem(-12px) rgba(0, 0, 0, 0.25); overflow: hidden; position: relative; color: #000; - @include mq(phone) { + @include mixins.mq(phone) { max-width: 80%; padding: 1rem; padding-bottom: 2rem; } - @include theme(light) { + @include mixins.theme(light) { color: #000; } @@ -38,14 +40,15 @@ li { color: #000; text-align: left; - @include theme(light) { + + @include mixins.theme(light) { color: #000; } } .small { - font-size: rem(11.2px); - line-height: rem(16px); + font-size: mixins.rem(11.2px); + line-height: mixins.rem(16px); } .Grid { @@ -54,14 +57,14 @@ flex-wrap: wrap; width: 60%; margin: 0 auto; - border-top: rem(0.08px) solid #ccc; - padding: rem(8px); + border-top: mixins.rem(0.08px) solid #ccc; + padding: mixins.rem(8px); - @include mq(s-phone) { + @include mixins.mq(s-phone) { width: 80% !important; } - @include mq(phone) { + @include mixins.mq(phone) { width: 70% !important; } @@ -78,32 +81,32 @@ appearance: none; -webkit-appearance: none; outline: none; - width: rem(50px); - height: rem(30px); + width: mixins.rem(50px); + height: mixins.rem(30px); background-color: #fff; - border: rem(1.008px) solid #d9dadc; - border-radius: rem(50px); - box-shadow: inset rem(-20px) 0 0 0 #fff; + border: mixins.rem(1.008px) solid #d9dadc; + border-radius: mixins.rem(50px); + box-shadow: inset mixins.rem(-20px) 0 0 0 #fff; &:after { content: ""; position: absolute; - top: rem(1.008px); - left: rem(1.008px); + top: mixins.rem(1.008px); + left: mixins.rem(1.008px); background: transparent; - width: rem(26px); - height: rem(26px); + width: mixins.rem(26px); + height: mixins.rem(26px); border-radius: 50%; - box-shadow: rem(2px) rem(4px) rem(6px) rgba(0, 0, 0, 0.2); + box-shadow: mixins.rem(2px) mixins.rem(4px) mixins.rem(6px) rgba(0, 0, 0, 0.2); } &:checked { - box-shadow: inset rem(20px) 0 0 0 var(--button-bg-re); + box-shadow: inset mixins.rem(20px) 0 0 0 var(--button-bg-re); border-color: var(--button-bg-re); &:after { left: 1.25rem; - box-shadow: rem(-2px) rem(4px) rem(3.008px) rgba(0, 0, 0, 0.05); + box-shadow: mixins.rem(-2px) mixins.rem(4px) mixins.rem(3.008px) rgba(0, 0, 0, 0.05); } } } @@ -117,11 +120,11 @@ .info { display: block; font-weight: 400; - font-size: rem(14.4px); + font-size: mixins.rem(14.4px); color: #595959; margin-top: 0; padding: 0; - line-height: rem(16px); + line-height: mixins.rem(16px); } } @@ -161,18 +164,18 @@ } .button { - font-size: rem(11.2px); + font-size: mixins.rem(11.2px); color: var(--button-fg-re); text-transform: uppercase; font-weight: bold; background: var(--button-bg-re); - padding: rem(3.2px); - padding-left: rem(16px); - padding-right: rem(16px); - border-radius: rem(48px); - margin-top: rem(12.8px) !important; + padding: mixins.rem(3.2px); + padding-left: mixins.rem(16px); + padding-right: mixins.rem(16px); + border-radius: mixins.rem(48px); + margin-top: mixins.rem(12.8px) !important; text-decoration: none; display: inline-block; cursor: pointer; } -} +} \ No newline at end of file diff --git a/src/styles/_modules/label.scss b/src/styles/_modules/_label.scss similarity index 62% rename from src/styles/_modules/label.scss rename to src/styles/_modules/_label.scss index 2b119c66..995986ba 100644 --- a/src/styles/_modules/label.scss +++ b/src/styles/_modules/_label.scss @@ -1,9 +1,11 @@ +@use "../_globals/mixins"; + /* Data Source label */ .source { font-weight: 400; - font-size: rem(12.8px); - padding-top: rem(16px); - line-height: rem(22.4px); + font-size: mixins.rem(12.8px); + padding-top: mixins.rem(16px); + line-height: mixins.rem(22.4px); display: block; a { diff --git a/src/styles/_modules/modals.scss b/src/styles/_modules/_modals.scss similarity index 56% rename from src/styles/_modules/modals.scss rename to src/styles/_modules/_modals.scss index c95fde2b..4914eb05 100644 --- a/src/styles/_modules/modals.scss +++ b/src/styles/_modules/_modals.scss @@ -1,13 +1,15 @@ +@use "../_globals/mixins"; + /* Modals */ .source { sup { font-size: 100%; - margin-left: rem(4.8px); + margin-left: mixins.rem(4.8px); } } sup { - margin: rem(1.6px); + margin: mixins.rem(1.6px); text-decoration: underline; cursor: pointer; } @@ -19,8 +21,8 @@ sup { .modal_view { background-color: var(--modal-bg); color: var(--modal-fg) !important; - border-top-right-radius: rem(8px); - border-top-left-radius: rem(8px); + border-top-right-radius: mixins.rem(8px); + border-top-left-radius: mixins.rem(8px); position: fixed; bottom: 0%; left: 0; @@ -29,10 +31,10 @@ sup { z-index: 9999999; overflow: overlay; text-align: left; - box-shadow: 0 rem(25px) rem(25px) rem(50px) rem(12px) rgba(0, 0, 0, 0.5); - padding-bottom: rem(16px); + box-shadow: 0 mixins.rem(25px) mixins.rem(25px) mixins.rem(50px) mixins.rem(12px) rgba(0, 0, 0, 0.5); + padding-bottom: mixins.rem(16px); - @include mq(desktop) { + @include mixins.mq(desktop) { height: auto; max-width: 40%; max-height: 50%; @@ -44,36 +46,36 @@ sup { .heading_img { width: 20%; - max-width: rem(48px); - margin-top: rem(32px); + max-width: mixins.rem(48px); + margin-top: mixins.rem(32px); margin-bottom: 0; } .pwa-install-prompt__guide__icon { - width: rem(16px); - padding-right: rem(4.8px); + width: mixins.rem(16px); + padding-right: mixins.rem(4.8px); } h2, h1 { font-weight: 600; - font-size: rem(16px); - margin-top: rem(24px); - padding-left: rem(24px); - padding-right: rem(24px); + font-size: mixins.rem(16px); + margin-top: mixins.rem(24px); + padding-left: mixins.rem(24px); + padding-right: mixins.rem(24px); } h1 { - font-size: rem(24px); - margin-top: rem(8px); + font-size: mixins.rem(24px); + margin-top: mixins.rem(8px); color: var(--modal-fg) !important; text-align: center !important; } p { color: var(--modal-fg) !important; - padding-left: rem(24px); - padding-right: rem(24px); + padding-left: mixins.rem(24px); + padding-right: mixins.rem(24px); font-size: var(--body-fontsize); } @@ -82,15 +84,15 @@ sup { } .button { - font-size: rem(16px); + font-size: mixins.rem(16px); color: var(--button-fg-re) !important; background: var(--button-bg-re); - padding: rem(16px); - padding-left: rem(16px); - padding-right: rem(16px); - border-radius: rem(48px); - margin-top: rem(12.8px) !important; - margin-bottom: rem(16px); + padding: mixins.rem(16px); + padding-left: mixins.rem(16px); + padding-right: mixins.rem(16px); + border-radius: mixins.rem(48px); + margin-top: mixins.rem(12.8px) !important; + margin-bottom: mixins.rem(16px); text-decoration: none; display: inline-block; } @@ -99,11 +101,11 @@ sup { text-align: left; .btn-dark { - width: rem(32px); - height: rem(32px); + width: mixins.rem(32px); + height: mixins.rem(32px); position: fixed; - right: rem(16px); - margin-top: rem(12.8px) !important; + right: mixins.rem(16px); + margin-top: mixins.rem(12.8px) !important; display: inline-block; min-width: auto; font-weight: 400; @@ -116,11 +118,11 @@ sup { border-radius: 60%; color: #000 !important; background-color: rgba(204, 204, 204, 0.6); - font-size: rem(20.8px); + font-size: mixins.rem(20.8px); z-index: 999999; - backdrop-filter: blur(rem(3.2px)); + backdrop-filter: blur(mixins.rem(3.2px)); - @include mq(desktop) { + @include mixins.mq(desktop) { right: 31%; } } @@ -128,14 +130,14 @@ sup { .share-btn { background: var(--modal-full-btn); - border-radius: rem(8px); - padding: rem(16px); - margin: rem(16px); + border-radius: mixins.rem(8px); + padding: mixins.rem(16px); + margin: mixins.rem(16px); display: flex; .share-text { display: block; - font-size: rem(19.2px); + font-size: mixins.rem(19.2px); width: 93%; } @@ -148,26 +150,26 @@ sup { .option { background: var(--modal-full-btn); border: var(--modal-border); - border-radius: rem(8px); - padding: rem(16px); - margin: rem(16px); + border-radius: mixins.rem(8px); + padding: mixins.rem(16px); + margin: mixins.rem(16px); .muted { color: #ccc; display: block; - font-size: rem(12.8px); + font-size: mixins.rem(12.8px); text-transform: uppercase; - padding-bottom: rem(16px); + padding-bottom: mixins.rem(16px); } .price { display: block; - font-size: rem(19.2px); + font-size: mixins.rem(19.2px); } } .active { - border: rem(1.6px) #016afe solid; + border: mixins.rem(1.6px) #016afe solid; } .center { @@ -182,35 +184,35 @@ sup { width: 100%; height: auto; background-color: var(--modal-bg-overlay); - backdrop-filter: blur(rem(3.2px)); + backdrop-filter: blur(mixins.rem(3.2px)); - @include mq(desktop) { + @include mixins.mq(desktop) { position: initial; } } .info { color: #9e9e9e; - font-size: rem(12.8px); + font-size: mixins.rem(12.8px); display: block; - margin: rem(16px); + margin: mixins.rem(16px); margin-top: 0; } .form-check-input { position: absolute; - right: rem(32px); + right: mixins.rem(32px); transform: scale(1.2); - @include mq(s-desktop) { + @include mixins.mq(s-desktop) { transform: scale(1.5); } - @include mq(s-phone) { + @include mixins.mq(s-phone) { transform: scale(1.2); } - @include mq(phone) { + @include mixins.mq(phone) { transform: scale(1.5); } } @@ -218,9 +220,9 @@ sup { /* Menu */ .menu { background: var(--modal-full-btn); - padding: rem(16px); - margin-left: rem(16px); - margin-right: rem(16px); + padding: mixins.rem(16px); + margin-left: mixins.rem(16px); + margin-right: mixins.rem(16px); border-radius: 0; border-bottom: var(--nav-border); display: flex; @@ -228,7 +230,7 @@ sup { .label { display: block; - font-size: rem(19.2px); + font-size: mixins.rem(19.2px); width: 95%; } @@ -239,20 +241,20 @@ sup { } .twitter { - border-top-right-radius: rem(8px); - border-top-left-radius: rem(8px); + border-top-right-radius: mixins.rem(8px); + border-top-left-radius: mixins.rem(8px); } .last { - border-bottom-right-radius: rem(8px); - border-bottom-left-radius: rem(8px); + border-bottom-right-radius: mixins.rem(8px); + border-bottom-left-radius: mixins.rem(8px); border-bottom: none; } } .info { color: #9e9e9e; - font-size: rem(12.8px); + font-size: mixins.rem(12.8px); display: block; margin-top: 0; } @@ -267,6 +269,6 @@ sup { position: absolute; left: 0; top: 0; - padding: rem(16px); + padding: mixins.rem(16px); text-decoration: none; } \ No newline at end of file diff --git a/src/styles/_modules/navbar.scss b/src/styles/_modules/_navbar.scss similarity index 69% rename from src/styles/_modules/navbar.scss rename to src/styles/_modules/_navbar.scss index f76cc150..b7b447e3 100644 --- a/src/styles/_modules/navbar.scss +++ b/src/styles/_modules/_navbar.scss @@ -1,3 +1,5 @@ +@use "../_globals/mixins"; + /* Navbar */ .nav { cursor: pointer; @@ -6,16 +8,16 @@ top: 0%; left: 50%; transform: translateX(-50%); - backdrop-filter: blur(rem(3.2px)); + backdrop-filter: blur(mixins.rem(3.2px)); background: var(--nav-bg); width: 100%; border-bottom: var(--nav-border); text-align: left; - height: rem(67.2px); + height: mixins.rem(67.2px); font-family: SF Pro Text, system-ui, -apple-system, BlinkMacSystemFont, Helvetica Neue, Helvetica, Arial, sans-serif; - @include mq(s-desktop) { + @include mixins.mq(s-desktop) { bottom: 0%; top: initial; border-top: var(--nav-border); @@ -28,29 +30,30 @@ align-items: center; justify-content: space-around; padding: 0; - padding-top: rem(8px); - padding-left: rem(16px); - padding-right: rem(16px); + padding-top: mixins.rem(8px); + padding-left: mixins.rem(16px); + padding-right: mixins.rem(16px); margin: 0; list-style: none; } .flex-item { - height: rem(40px); + height: mixins.rem(40px); margin: 0; - padding: rem(1.6px); + padding: mixins.rem(1.6px); text-align: center; color: #8e8e93; - font-size: rem(16px); + font-size: mixins.rem(16px); - @include theme(light) { + @include mixins.theme(light) { color: #000; } .icon { &:before { color: #8e8e93 !important; - @include theme(light) { + + @include mixins.theme(light) { color: #000; } } @@ -60,14 +63,15 @@ .active { color: #fff !important; - @include theme(light) { + @include mixins.theme(light) { color: #007aff !important; } .icon { &:before { color: #fff !important; - @include theme(light) { + + @include mixins.theme(light) { color: #007aff !important; } } @@ -76,7 +80,7 @@ .menu-item { display: block; - padding-top: rem(4.8px); + padding-top: mixins.rem(4.8px); } a, @@ -85,4 +89,4 @@ text-decoration: none; color: inherit; } -} +} \ No newline at end of file diff --git a/src/styles/_modules/nutriscore.scss b/src/styles/_modules/_nutriscore.scss similarity index 74% rename from src/styles/_modules/nutriscore.scss rename to src/styles/_modules/_nutriscore.scss index e260d4e6..2f549fb5 100644 --- a/src/styles/_modules/nutriscore.scss +++ b/src/styles/_modules/_nutriscore.scss @@ -1,14 +1,16 @@ +@use "../_globals/mixins"; + .labels { - padding: rem(8px) !important; - width: rem(64px) !important; + padding: mixins.rem(8px) !important; + width: mixins.rem(64px) !important; height: auto; - @include mq(s-phone) { + @include mixins.mq(s-phone) { padding: 0.5rem !important; width: 2.5rem !important; } - @include mq(phone) { + @include mixins.mq(phone) { width: 3rem !important; } } @@ -19,6 +21,10 @@ color: var(--non-vegan); } +.maybe-vegan { + color: var(--maybe-vegan); +} + .vegan, .icon-ok { color: var(--vegan); @@ -66,8 +72,8 @@ } .warning { - @include mq(phone) { + @include mixins.mq(phone) { font-size: 0.6rem !important; line-height: 1rem !important; } -} +} \ No newline at end of file diff --git a/src/styles/_modules/overlays.scss b/src/styles/_modules/_overlays.scss similarity index 76% rename from src/styles/_modules/overlays.scss rename to src/styles/_modules/_overlays.scss index 9bae79bb..5eb69af6 100644 --- a/src/styles/_modules/overlays.scss +++ b/src/styles/_modules/_overlays.scss @@ -1,3 +1,5 @@ +@use "../_globals/mixins"; + /* Rotate device screen */ .rotate { z-index: 99999999999999999999; @@ -12,8 +14,8 @@ display: none; img { - padding-top: rem(80px); - height: rem(128px); + padding-top: mixins.rem(80px); + height: mixins.rem(128px); } } @@ -30,6 +32,6 @@ height: 100%; h3 { - padding-top: rem(80px); + padding-top: mixins.rem(80px); } -} +} \ No newline at end of file diff --git a/src/styles/_modules/scanner.scss b/src/styles/_modules/_scanner.scss similarity index 67% rename from src/styles/_modules/scanner.scss rename to src/styles/_modules/_scanner.scss index 182439ac..b47fd875 100644 --- a/src/styles/_modules/scanner.scss +++ b/src/styles/_modules/_scanner.scss @@ -1,3 +1,5 @@ +@use "../_globals/mixins"; + /* Scanner */ .eanscanner { z-index: 999999 !important; @@ -19,7 +21,7 @@ color: #fff; left: 50%; - @include mq(phone) { + @include mixins.mq(phone) { width: 101%; } @@ -30,16 +32,16 @@ justify-content: space-between; padding: 0; margin: 0; - margin-left: rem(16px); - margin-right: rem(16px); + margin-left: mixins.rem(16px); + margin-right: mixins.rem(16px); list-style: none; } .flex-item { margin: 0; padding: 0; - padding-top: rem(3.2px); - font-size: rem(20.8px); + padding-top: mixins.rem(3.2px); + font-size: mixins.rem(20.8px); &:last-child { float: right; @@ -48,19 +50,19 @@ } .middle { - font-size: rem(16px); + font-size: mixins.rem(16px); font-weight: 300; } .button { - font-size: rem(11.2px); + font-size: mixins.rem(11.2px); color: var(--button-fg); text-transform: uppercase; background: var(--button-bg); - padding: rem(8px); - padding-left: rem(24px); - padding-right: rem(24px); - border-radius: rem(48px); + padding: mixins.rem(8px); + padding-left: mixins.rem(24px); + padding-right: mixins.rem(24px); + border-radius: mixins.rem(48px); } } @@ -68,16 +70,16 @@ position: fixed; left: 50%; transform: translateX(-50%); - bottom: rem(32px); - font-size: rem(19.2px); + bottom: mixins.rem(32px); + font-size: mixins.rem(19.2px); z-index: 99999999999999999; color: var(--button-fg); text-transform: uppercase; background: var(--button-bg); - padding: rem(8px); - padding-left: rem(24px); - padding-right: rem(24px); - border-radius: rem(48px); + padding: mixins.rem(8px); + padding-left: mixins.rem(24px); + padding-right: mixins.rem(24px); + border-radius: mixins.rem(48px); transition: 0.8s; &:hover, @@ -94,14 +96,14 @@ top: 30%; left: 50%; opacity: 0.4; - font-size: rem(128px); + font-size: mixins.rem(128px); -webkit-transform: translateX(-50%); transform: translateX(-50%); } /* Scanner Underlay */ #background { - backdrop-filter: blur(rem(48px)); + backdrop-filter: blur(mixins.rem(48px)); z-index: 999; position: fixed; padding: 0; @@ -114,13 +116,13 @@ .name { display: block; - padding-bottom: rem(16px); + padding-bottom: mixins.rem(16px); - @include mq(s-phone) { + @include mixins.mq(s-phone) { display: inline-block; } - @include mq(phone) { + @include mixins.mq(phone) { display: inline-block; } -} +} \ No newline at end of file diff --git a/src/styles/_modules/skeleton.scss b/src/styles/_modules/_skeleton.scss similarity index 71% rename from src/styles/_modules/skeleton.scss rename to src/styles/_modules/_skeleton.scss index 863e74c7..253e37ba 100644 --- a/src/styles/_modules/skeleton.scss +++ b/src/styles/_modules/_skeleton.scss @@ -1,3 +1,5 @@ +@use "../_globals/mixins"; + .skeleton { display: block; width: fit-content; @@ -10,22 +12,21 @@ padding-bottom: 0; cursor: default !important; background: rgba(130, 130, 130, 0.2); - background: linear-gradient( - to right, - rgba(130, 130, 130, 0.2) 8%, - rgba(130, 130, 130, 0.3) 18%, - rgba(130, 130, 130, 0.2) 33% - ) !important; - background-size: rem(800px) rem(100px) !important; + background: linear-gradient(to right, + rgba(130, 130, 130, 0.2) 8%, + rgba(130, 130, 130, 0.3) 18%, + rgba(130, 130, 130, 0.2) 33%) !important; + background-size: mixins.rem(800px) mixins.rem(100px) !important; animation: skeleton 2s infinite ease-out; } @keyframes skeleton { 0% { - background-position: rem(-468px) 0; + background-position: mixins.rem(-468px) 0; } + 100% { - background-position: rem(468px) 0; + background-position: mixins.rem(468px) 0; } } @@ -35,12 +36,14 @@ margin-bottom: 1rem !important; width: 30%; } + .description.skeleton { color: transparent; margin-left: 0; padding: 0; max-width: fit-content; - @include mq(s-phone) { + + @include mixins.mq(s-phone) { max-width: 60%; white-space: nowrap; } @@ -50,6 +53,7 @@ margin-right: 0; max-width: 5%; } + .source.skeleton { margin-top: 1rem; padding-top: 0; @@ -65,7 +69,8 @@ width: 10%; } } + .button.skeleton { color: transparent; } -} +} \ No newline at end of file diff --git a/src/styles/_modules/_tooltip.scss b/src/styles/_modules/_tooltip.scss new file mode 100644 index 00000000..b6f65314 --- /dev/null +++ b/src/styles/_modules/_tooltip.scss @@ -0,0 +1,45 @@ +@use "../_globals/mixins"; + +.tooltip-wrapper { + margin: 0 auto; + position: relative; + display: block; + + .Grid:first-child { + border-top: .005rem solid #ccc; + } +} + +.tooltip { + position: absolute; + bottom: 100%; + left: 50%; + transform: translateX(-50%); + margin-bottom: 0.3125rem; + padding: 0.5rem 0.75rem; + background-color: #333; + color: white; + font-size: 0.875rem; + border-radius: 0.25rem; + z-index: 1000; + white-space: wrap; + line-height: 1.25rem; + pointer-events: none; + width: fit-content; + + @include mixins.mq(phone) { + width: 90% !important; + } + + .tooltip-arrow { + position: absolute; + bottom: -0.3125rem; + left: 50%; + transform: translateX(-50%); + width: 0; + height: 0; + border-left: 0.3125rem solid transparent; + border-right: 0.3125rem solid transparent; + border-top: 0.3125rem solid #333; + } +} \ No newline at end of file diff --git a/src/styles/_pages/body.scss b/src/styles/_pages/_body.scss similarity index 65% rename from src/styles/_pages/body.scss rename to src/styles/_pages/_body.scss index e89d4b4f..70247cb8 100644 --- a/src/styles/_pages/body.scss +++ b/src/styles/_pages/_body.scss @@ -1,3 +1,5 @@ +@use "../_globals/mixins"; + /* Selection color */ ::-moz-selection { background-color: #353b48; @@ -19,7 +21,7 @@ body { text-align: center; word-wrap: break-word; - @include theme(light) { + @include mixins.theme(light) { background-color: #7f8fa6; } } @@ -40,7 +42,7 @@ body { h1 { color: var(--fg-color); - @include mq(phone) { + @include mixins.mq(phone) { font-size: 1.8rem; } } @@ -76,21 +78,21 @@ input[type="number"] { -moz-appearance: textfield; appearance: textfield; width: 55%; - padding: rem(16px); - padding-top: rem(15px); + padding: mixins.rem(16px); + padding-top: mixins.rem(15px); text-align: center; color: #000; font-weight: var(--body-fontweight); flex: 1; - border-top: rem(0.16px) solid #ccc; - border-bottom: rem(0.16px) solid #ccc; + border-top: mixins.rem(0.16px) solid #ccc; + border-bottom: mixins.rem(0.16px) solid #ccc; border-left: none; border-right: none; border-radius: 0; - @include mq(phone) { - padding-top: rem(14px); - padding-bottom: rem(15px); + @include mixins.mq(phone) { + padding-top: mixins.rem(14px); + padding-bottom: mixins.rem(15px); } } @@ -101,9 +103,9 @@ textarea { color: #000; font-weight: var(--body-fontweight); flex: 1; - border-top: rem(0.16px) solid #ccc; - border-bottom: rem(0.16px) solid #ccc; - border-left: rem(0.16px) solid #ccc; + border-top: mixins.rem(0.16px) solid #ccc; + border-bottom: mixins.rem(0.16px) solid #ccc; + border-left: mixins.rem(0.16px) solid #ccc; border-right: none; border-radius: 0; resize: vertical; @@ -111,38 +113,40 @@ textarea { /* Container */ .container { - padding-top: rem(150px); + padding-top: mixins.rem(150px); /* padding-bottom: rem(50px);*/ width: 50%; margin: 0 auto; - @include mq(phone) { + @include mixins.mq(phone) { padding-top: 4rem; width: 100%; } img { - width: rem(48px); - height: rem(48px); + width: mixins.rem(48px); + height: mixins.rem(48px); } } .top { - @include mq(phone) { + @include mixins.mq(phone) { padding-top: 0.5rem; } - @include mq(s-phone) { + + @include mixins.mq(s-phone) { padding-top: 0.5rem; } - @include mq(s-desktop) { + + @include mixins.mq(s-desktop) { padding-top: 2rem; } } .logo { - padding-bottom: rem(32px); + padding-bottom: mixins.rem(32px); - @include mq(phone) { + @include mixins.mq(phone) { padding-bottom: 1rem; padding-top: 1rem; } @@ -150,7 +154,7 @@ textarea { /* Longpage for Privacy/Imprint */ .longpage { - padding-top: rem(50px) !important; + padding-top: mixins.rem(50px) !important; color: #000; a, @@ -166,23 +170,24 @@ p.missing { } p { - line-height: rem(24px); - @include theme(light) { + line-height: mixins.rem(24px); + + @include mixins.theme(light) { color: #fff; } } /* Footer */ footer { - font-size: rem(13.6px); + font-size: mixins.rem(13.6px); padding-bottom: 4.8rem; - @include mq(s-phone) { + @include mixins.mq(s-phone) { font-size: 0.6rem; margin-bottom: 1rem; } - @include mq(phone) { + @include mixins.mq(phone) { font-size: 0.7rem; margin: 0 auto; margin-bottom: 1rem; @@ -190,7 +195,7 @@ footer { } a { - @include theme(light) { + @include mixins.theme(light) { color: #fff; } } @@ -198,7 +203,8 @@ footer { a:hover, a:focus { color: #7f8fa6; - @include theme(light) { + + @include mixins.theme(light) { color: #fff; } } @@ -214,4 +220,4 @@ table { table tbody, thead { text-align: left; -} +} \ No newline at end of file diff --git a/src/styles/_pages/frontpage.scss b/src/styles/_pages/_frontpage.scss similarity index 90% rename from src/styles/_pages/frontpage.scss rename to src/styles/_pages/_frontpage.scss index 0b980da4..efca242b 100644 --- a/src/styles/_pages/frontpage.scss +++ b/src/styles/_pages/_frontpage.scss @@ -1,3 +1,5 @@ +@use "../_globals/mixins"; + #result { height: auto; -webkit-box-orient: vertical; @@ -9,7 +11,7 @@ line-height: 2rem; padding-bottom: 1rem; - @include mq(phone) { + @include mixins.mq(phone) { font-size: 1rem !important; line-height: 1.5rem; } @@ -78,11 +80,11 @@ /* Make sure mobile functions are not displayed on desktop*/ .icon-flipcamera { - @include mq(desktop) { + @include mixins.mq(desktop) { display: none; } - @include mq(phone) { + @include mixins.mq(phone) { display: inline-block; } -} +} \ No newline at end of file diff --git a/src/styles/_pages/more.scss b/src/styles/_pages/_more.scss similarity index 100% rename from src/styles/_pages/more.scss rename to src/styles/_pages/_more.scss diff --git a/src/styles/font/vegancheckicons 2.eot b/src/styles/font/vegancheckicons 2.eot new file mode 100644 index 00000000..356c7130 Binary files /dev/null and b/src/styles/font/vegancheckicons 2.eot differ diff --git a/src/styles/font/vegancheckicons.eot b/src/styles/font/vegancheckicons.eot index 356c7130..17129a15 100644 Binary files a/src/styles/font/vegancheckicons.eot and b/src/styles/font/vegancheckicons.eot differ diff --git a/src/styles/font/vegancheckicons.svg b/src/styles/font/vegancheckicons.svg index 6bcd7f41..09e5d3d4 100644 --- a/src/styles/font/vegancheckicons.svg +++ b/src/styles/font/vegancheckicons.svg @@ -1 +1,42 @@ -Copyright (C) 2022 by original authors @ fontello.com \ No newline at end of file + + + +Copyright (C) 2024 by original authors @ fontello.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/styles/font/vegancheckicons.ttf b/src/styles/font/vegancheckicons.ttf index 56dbdc34..2ce613f3 100644 Binary files a/src/styles/font/vegancheckicons.ttf and b/src/styles/font/vegancheckicons.ttf differ diff --git a/src/styles/font/vegancheckicons.woff b/src/styles/font/vegancheckicons.woff index 8303bae2..ee63ab0a 100644 Binary files a/src/styles/font/vegancheckicons.woff and b/src/styles/font/vegancheckicons.woff differ diff --git a/src/styles/font/vegancheckicons.woff2 b/src/styles/font/vegancheckicons.woff2 index 66b04c1f..6fadd388 100644 Binary files a/src/styles/font/vegancheckicons.woff2 and b/src/styles/font/vegancheckicons.woff2 differ diff --git a/src/styles/font/vegancheckicons_config.json b/src/styles/font/vegancheckicons_config.json index 87703df0..d77f0305 100644 --- a/src/styles/font/vegancheckicons_config.json +++ b/src/styles/font/vegancheckicons_config.json @@ -104,6 +104,12 @@ "code": 59408, "src": "fontawesome" }, + { + "uid": "00391fac5d419345ffcccd95b6f76263", + "css": "attention-alt", + "code": 61738, + "src": "fontawesome" + }, { "uid": "98bf291fc304e3f3ab58529eba9bb24d", "css": "vegancheck", diff --git a/src/styles/style.scss b/src/styles/style.scss index 9cdcaed7..294ef4dc 100644 --- a/src/styles/style.scss +++ b/src/styles/style.scss @@ -1,8 +1,25 @@ /* Import scss structure */ -@import '_globals/normalize', '_globals/animate', '_globals/vegancheckicons', '_globals/mixins', '_globals/fonts', '_globals/roots'; +@use '_globals/normalize'; +@use '_globals/animate'; +@use '_globals/vegancheckicons'; +@use '_globals/mixins' as *; +@use '_globals/fonts'; +@use '_globals/roots'; /* Import scss pages */ -@import '_pages/body', '_pages/frontpage', '_pages/more'; +@use '_pages/body'; +@use '_pages/frontpage'; +@use '_pages/more'; /* Import modules */ -@import '_modules/modals', '_modules/form', '_modules/label', '_modules/scanner', '_modules/overlays', '_modules/buttons', '_modules/banner', '_modules/navbar', '_modules/nutriscore', '_modules/skeleton'; \ No newline at end of file +@use '_modules/modals'; +@use '_modules/form'; +@use '_modules/label'; +@use '_modules/scanner'; +@use '_modules/overlays'; +@use '_modules/buttons'; +@use '_modules/banner'; +@use '_modules/navbar'; +@use '_modules/nutriscore'; +@use '_modules/skeleton'; +@use '_modules/tooltip'; \ No newline at end of file