diff --git a/.stylelintrc.json b/.stylelintrc.json index 3cf898f..7d73826 100644 --- a/.stylelintrc.json +++ b/.stylelintrc.json @@ -1,3 +1,6 @@ { - "extends": ["stylelint-config-standard-scss"] + "extends": ["stylelint-config-standard-scss"], + "rules": { + "selector-class-pattern": "^([a-z][a-z0-9]*)((-|__|--)[a-z0-9]+)*$" + } } diff --git a/.svgo.config.js b/.svgo.config.js new file mode 100644 index 0000000..46a3a1e --- /dev/null +++ b/.svgo.config.js @@ -0,0 +1,40 @@ +module.exports = { + plugins: [ + // Remove unnecessary metadata + 'removeDoctype', + 'removeXMLProcInst', + 'removeComments', + 'removeMetadata', + 'removeEditorsNSData', + + // Remove unused elements + 'removeUselessDefs', + 'removeEmptyAttrs', + 'removeEmptyText', + 'removeEmptyContainers', + 'removeUnknownsAndDefaults', + + // Optimize paths and shapes + 'convertShapeToPath', + 'mergePaths', + 'convertPathData', + 'removeUselessStrokeAndFill', + + // Clean up structure + 'cleanupAttrs', + 'cleanupNumericValues', + 'cleanupListOfValues', + 'convertStyleToAttrs', + 'removeStyleElement', + + // Optimize for size + 'collapseGroups', + 'moveElemsAttrsToGroup', + 'moveGroupAttrsToElems', + 'sortAttrs', + + // Transform optimizations + 'removeOffCanvasPaths', + 'reusePaths' + ] +} \ No newline at end of file diff --git a/assets/critical.css b/assets/critical.css index 4578fb0..ccedbcf 100644 --- a/assets/critical.css +++ b/assets/critical.css @@ -1 +1 @@ -button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}button,[type=button],[type=reset],[type=submit]{appearance:button}button::-moz-focus-inner,[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner{border-style:none;padding:0}button:-moz-focusring,[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto;font-family:inherit}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{appearance:none}::-webkit-autofill,::-webkit-autofill:active,::-webkit-autofill:hover,::-webkit-autofill:focus{box-shadow:0 0 0 1000px rgba(0,0,0,0) inset !important;background-clip:text !important;color:var(--color-primary, #000) !important;-webkit-text-fill-color:var(--color-primary, #000) !important}::-webkit-file-upload-button{appearance:button;font:inherit}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:var(--font-heading);font-weight:var(--font-weight-headers);line-height:var(--font-height-sm);margin-bottom:1.5625rem;letter-spacing:-1px;color:currentcolor}h1,.h1{font-size:var(--font-size-h1);font-weight:var(--font-weight-h1)}h2,.h2{font-size:var(--font-size-h2);font-weight:var(--font-weight-h2)}h3,.h3{font-size:var(--font-size-h3);font-weight:var(--font-weight-h3)}h4,.h4{font-size:32px;line-height:var(--font-height-md)}h5,.h5{font-size:24px;line-height:var(--font-height-lg)}h6,.h6{font-size:20px;line-height:var(--font-height-lg)}p{font-family:inherit;font-size:inherit;line-height:inherit;margin-bottom:1.25rem}em,i{font-family:inherit;font-size:inherit;line-height:inherit;font-style:italic}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-0.25em}sup{top:-0.5em}b,strong{font-weight:var(--font-weight-bold)}a{display:inline-block;vertical-align:top;background:none;color:inherit;text-decoration:none;transition:all var(--transition-duration) ease-in-out}a:hover{opacity:.8}ul,ol{padding:0;margin:0}ul{list-style:none}blockquote,q,cite{font-family:inherit;font-size:calc(var(--font-size-base) + 2px);font-weight:var(--font-weight-regular);line-height:inherit}q{font-size:var(--font-size-base);font-weight:var(--font-weight-semibold)}cite{font-size:var(--font-size-base)}pre{font-family:monospace;font-size:1em;white-space:pre-wrap;margin:1em 0}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}code,kbd,samp{font-family:monospace;font-size:1em}hr{box-sizing:content-box;height:0;overflow:visible}.fa,.far{color:currentcolor}*,*::before,*::after{margin:0;padding:0;box-sizing:border-box}html{overflow-x:clip;text-size-adjust:100%;height:100%}body{display:flex;flex-direction:column;color:var(--color-primary, #000);background:var(--background-primary, #fff);margin:0;padding:0;min-height:100%;width:100%;font-family:var(--font-body);font-size:var(--font-size-base);line-height:var(--font-height-xl);overflow-x:clip;overflow-wrap:anywhere;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}body>*{flex:0 0 auto}.container{max-width:1920px;padding:0 16px;margin:0 auto}@media(min-width: 1440px){.container{padding:0 80px}}.tagline{display:inline-block;vertical-align:top;font-family:var(--font-tagline);font-weight:var(--font-weight-semibold);line-height:var(--font-height-lg);font-size:var(--font-size-tagline);color:var(--color-outline);margin:0 0 20px}@media(min-width: 992px){.tagline{margin:0 0 16px}}img{display:block;height:auto;max-width:100%}#main{flex:1 1 auto;display:flex;flex-direction:column}#main>*{width:100%}.overflow-hidden{overflow:clip}[hidden]{display:none} +button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}button,[type=button],[type=reset],[type=submit]{appearance:button}button::-moz-focus-inner,[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner{border-style:none;padding:0}button:-moz-focusring,[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto;font-family:inherit}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{appearance:none}::-webkit-autofill,::-webkit-autofill:active,::-webkit-autofill:hover,::-webkit-autofill:focus{box-shadow:0 0 0 1000px rgba(0,0,0,0) inset !important;background-clip:text !important;color:var(--color-primary, #000) !important;-webkit-text-fill-color:var(--color-primary, #000) !important}::-webkit-file-upload-button{appearance:button;font:inherit}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:var(--font-heading);font-weight:var(--font-weight-headers);line-height:1.25;margin-bottom:1.5625rem;letter-spacing:-1px;color:currentcolor}h1,.h1{font-size:var(--font-size-h1);font-weight:var(--font-weight-h1)}h2,.h2{font-size:var(--font-size-h2);font-weight:var(--font-weight-h2)}h3,.h3{font-size:var(--font-size-h3);font-weight:var(--font-weight-h3)}h4,.h4{font-size:32px;line-height:1.3333}h5,.h5{font-size:24px;line-height:1.4}h6,.h6{font-size:20px;line-height:1.4}p{font-family:inherit;font-size:inherit;line-height:inherit;margin-bottom:1.25rem}em,i{font-family:inherit;font-size:inherit;line-height:inherit;font-style:italic}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-0.25em}sup{top:-0.5em}b,strong{font-weight:var(--font-weight-bold)}a{--link-color: inherit;display:inline-block;vertical-align:top;background:none;color:var(--link-color);text-decoration:none;transition:all var(--transition-duration) var(--transition-function)}a:hover{--link-color: var(--color-outline)}ul,ol{padding:0;margin:0}ul{list-style:none}blockquote,q,cite{font-family:inherit;font-size:calc(var(--font-size-base) + 2px);font-weight:var(--font-weight-regular);line-height:inherit}q{font-size:var(--font-size-base);font-weight:var(--font-weight-semibold)}cite{font-size:var(--font-size-base)}pre{font-family:monospace;font-size:1em;white-space:pre-wrap;margin:1em 0}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}code,kbd,samp{font-family:monospace;font-size:1em}hr{box-sizing:content-box;height:0;overflow:visible}.fa,.far,[class^=fa-],[class*=fa-]{color:currentcolor;transition:all var(--transition-duration) var(--transition-function)}.logo{position:relative}.logo__image{display:flex;align-items:center;max-height:64px;max-width:200px;width:auto}.logo__image-holder--floated{position:absolute;top:50%;left:0;transform:translateY(-50%)}.logo__text{font-size:24px;line-height:1.3333;font-weight:var(--font-weight-semibold);margin:0;color:currentcolor}*,*::before,*::after{margin:0;padding:0;box-sizing:border-box}html{overflow-x:clip;text-size-adjust:100%;height:100%}body{display:flex;flex-direction:column;position:relative;color:var(--color-primary, #000);background:var(--background-primary, #fff);margin:0;padding:0;min-height:100%;width:100%;font-family:var(--font-body);font-size:var(--font-size-base);line-height:1.5;overflow-x:clip;overflow-wrap:anywhere;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}body>*{flex:0 0 auto}.container{max-width:1920px;padding:0 16px;margin:0 auto}@media(min-width: 1440px){.container{padding:0 80px}}.tagline{display:inline-block;vertical-align:top;font-family:var(--font-tagline);font-weight:var(--font-weight-semibold);line-height:1.4;font-size:var(--font-size-tagline);color:var(--color-outline);margin:0 0 20px}@media(min-width: 992px){.tagline{margin:0 0 16px}}img{display:block;height:auto;max-width:100%}#main{flex:1 1 auto;display:flex;flex-direction:column}#main>*{width:100%}.overflow-hidden{overflow:clip}[hidden]{display:none} diff --git a/assets/example.js b/assets/example.js deleted file mode 100644 index 366cf91..0000000 --- a/assets/example.js +++ /dev/null @@ -1,3 +0,0 @@ -// Example JavaScript file - replace with actual functionality -// eslint-disable-next-line no-console -console.log('Example JavaScript file loaded successfully!') diff --git a/assets/header.css b/assets/header.css new file mode 100644 index 0000000..b810814 --- /dev/null +++ b/assets/header.css @@ -0,0 +1 @@ +.menu{--menu-display: flex;--menu-dropdown-events: none;--menu-dropdown-height: calc(100vh - var(--header-height, 65px));--menu-dropdown-item-display: grid;--menu-dropdown-left: auto;--menu-dropdown-link-display: flex;--menu-dropdown-link-font-size: 18px;--menu-dropdown-link-font-weight: var(--font-weight-bold);--menu-dropdown-link-indent: 0;--menu-dropdown-link-padding: 16px var(--menu-dropdown-link-indent) 16px 0;--menu-dropdown-position: fixed;--menu-dropdown-right: 0;--menu-dropdown-top: 0;--menu-dropdown-transform: translateX(100%);--menu-dropdown-width: 100%;--menu-dropdown-opener-display: flex;--menu-dropdown-opener-height: var(--button-height);--menu-dropdown-opener-position: center;--menu-dropdown-opener-transform: rotate(90deg);--menu-dropdown-opener-weight: inherit;--menu-dropdown-opener-width: var(--button-width);--menu-dropdown-wrapper-height: 100%;--menu-dropdown-wrapper-margin: 0;--menu-dropdown-wrapper-overflow: clip auto;--menu-dropdown-wrapper-padding: 25px 16px 50px;--menu-dropdown-wrapper-position: absolute;--menu-dropdown-wrapper-transform: translateX(100%);--menu-dropdown-wrapper-width: 100vw;--menu-opener-color: currentcolor;--menu-horizontal-position: flex-end;--menu-item-cursor: default;--menu-item-margin: 0;--menu-item-icon-transform: rotate(-90deg);--menu-item-icon-margin: 0;--menu-link-indent: 0;--menu-list-margin: 0 auto;--menu-list-overflow: clip auto;--menu-list-padding: 0;--menu-list-width: 100%;--menu-margin-bottom: var(--padding-bottom-mobile, 12px);--menu-margin-top: var(--padding-top-mobile, 12px);--menu-nav-delay: 50ms;--menu-nav-duration: var(--transition-duration);--menu-nav-events: none;--menu-nav-height: calc(100vh - var(--header-height, 65px));--menu-nav-overflow: clip auto;--menu-nav-visibility: hidden;--menu-nav-width: 100%;--menu-opener-display: flex;--menu-opener-icon-left: 27%;--menu-opener-icon-right: 27%;--menu-opener-icon-top: 50%;--menu-opener-icon-transform: translate(0, -50%);--menu-wrapper-background: var(--background-primary, #fff);--menu-wrapper-flex-direction: column;--menu-wrapper-overflow: clip;--menu-wrapper-padding: 16px;--menu-wrapper-position: absolute;--menu-wrapper-transform: translateY(-100%);--menu-wrapper-width: 100vw;display:var(--menu-display);align-items:center}@media(min-width: 992px){.menu{--menu-display: block;--menu-dropdown-item-display: block;--menu-dropdown-position: absolute;--menu-dropdown-top: calc(100% + 1px);--menu-dropdown-width: 100vw;--menu-dropdown-height: auto;--menu-dropdown-left: 50%;--menu-dropdown-link-display: inline-block;--menu-dropdown-link-font-size: 20px;--menu-dropdown-link-padding: 0;--menu-dropdown-right: auto;--menu-dropdown-transform: translateX(-50%);--menu-dropdown-item-margin: 0;--menu-dropdown-opener-height: auto;--menu-dropdown-opener-width: auto;--menu-dropdown-list-display: grid;--menu-dropdown-list-gap: 20px;--menu-dropdown-list-padding: 25px 80px;--menu-dropdown-wrapper-height: auto;--menu-dropdown-wrapper-margin: 0 auto;--menu-dropdown-wrapper-overflow: auto;--menu-dropdown-wrapper-padding: 0;--menu-dropdown-wrapper-position: static;--menu-dropdown-wrapper-transform: translateY(-100%);--menu-dropdown-wrapper-width: 100%;--menu-horizontal-position: var(--menu-horizontal-position-desktop, center);--menu-item-margin: 0 22px;--menu-list-overflow: inherit;--menu-list-margin: calc(-1 * var(--menu-margin-top)) var(--menu-margin-x-desktop) calc(-1 * var(--menu-margin-bottom));--menu-list-width: calc(100% + -1 * var(--menu-margin-x-desktop, 0) * 2);--menu-margin-bottom: var(--padding-bottom-desktop, 12px);--menu-margin-top: var(--padding-top-desktop, 12px);--menu-nav-delay: 0s;--menu-nav-duration: 0s;--menu-nav-events: all;--menu-nav-height: 100%;--menu-nav-overflow: inherit;--menu-nav-visibility: visible;--menu-nav-width: auto;--menu-opener-display: none;--menu-wrapper-background: none;--menu-wrapper-flex-direction: row;--menu-wrapper-overflow: inherit;--menu-wrapper-padding: 0;--menu-wrapper-position: static;--menu-wrapper-transform: translateY(0);--menu-wrapper-width: 100%}}.menu path{stroke:var(--menu-opener-color);stroke-width:1;transition:stroke var(--transition-duration) var(--transition-function)}.menu__opener{height:var(--button-height);width:var(--button-width);border-radius:var(--button-border-radius);display:var(--menu-opener-display);align-items:center;background:color-mix(in srgb, var(--color-primary) 15%, transparent);position:relative;cursor:pointer}.menu__opener span,.menu__opener::before,.menu__opener::after{background:var(--menu-opener-color);position:absolute;top:var(--menu-opener-icon-top);left:var(--menu-opener-icon-left);right:var(--menu-opener-icon-right);height:2px;transform:var(--menu-opener-icon-transform);transition:all var(--transition-duration) var(--transition-function);pointer-events:none}#header-opener-menu:checked~.menu__opener span{opacity:0}.menu__opener::before{--menu-opener-icon-top: calc(50% - 6px);--menu-opener-icon-transform: translate(0, 0);content:""}#header-opener-menu:checked~.menu__opener::before{--menu-opener-icon-transform: rotate(45deg);--menu-opener-icon-top: 50%;--menu-opener-icon-left: 23%;--menu-opener-icon-right: 23%}.menu__opener::after{--menu-opener-icon-top: calc(50% + 4px);--menu-opener-icon-transform: translate(0, 0);content:""}#header-opener-menu:checked~.menu__opener::after{--menu-opener-icon-transform: rotate(-45deg);--menu-opener-icon-top: 50%;--menu-opener-icon-left: 23%;--menu-opener-icon-right: 23%}.menu__opener:hover::before,.menu__opener:hover::after,.menu__opener:hover span{--menu-opener-color: var(--color-outline)}.menu__wrapper{position:var(--menu-wrapper-position);width:var(--menu-wrapper-width);min-height:100%;top:0;left:0;z-index:1;transform:var(--menu-wrapper-transform);transition:transform var(--transition-duration) var(--transition-function);display:flex;flex-direction:var(--menu-wrapper-flex-direction);justify-content:space-between;overflow:var(--menu-wrapper-overflow);color:var(--color-primary, #000);padding:var(--menu-wrapper-padding);background:var(--menu-wrapper-background)}@media(min-width: 992px){.menu__wrapper{height:100%}}.menu__wrapper>span:not(:only-child),.menu__wrapper>ul:not(:only-child),.menu__wrapper>div:not(:only-child){padding:0 0 50px}@media(min-width: 992px){.menu__wrapper>span:not(:only-child),.menu__wrapper>ul:not(:only-child),.menu__wrapper>div:not(:only-child){padding:0}}.menu__nav{position:var(--menu-wrapper-position);height:var(--menu-nav-height);width:var(--menu-nav-width);top:var(--header-height, 65px);left:0;overflow:var(--menu-nav-overflow);pointer-events:var(--menu-nav-events);visibility:var(--menu-nav-visibility);transition:visibility var(--menu-nav-duration) var(--transition-function) var(--menu-nav-delay);font-weight:var(--font-weight-regular);display:block !important}#header-opener-menu:checked~.menu__nav{--menu-nav-events: all;--menu-nav-visibility: visible;--menu-nav-duration: 0s;--menu-nav-delay: 0s}#header-opener-menu:checked~.menu__nav .menu__wrapper{--menu-wrapper-transform: translateY(0)}.menu__list{list-style-type:none;position:relative;max-width:1920px;height:auto;width:var(--menu-list-width);overflow:var(--menu-list-overflow);margin:var(--menu-list-margin);padding:var(--menu-list-padding);flex:1 1 auto}@media(min-width: 992px){.menu__list{display:flex;justify-content:var(--menu-horizontal-position);align-items:center}}.menu__list:not(:only-child){--menu-list-padding: 0 0 50px}.menu__item{display:grid;grid-template-columns:1fr min-content;grid-template-areas:"item-1 item-2";align-items:center;margin:var(--menu-item-margin);cursor:var(--menu-item-cursor)}@media(min-width: 992px){.menu__item{height:100%}}.menu__item-icon{display:flex;align-items:center;justify-content:center;margin:var(--menu-item-icon-margin);transform:var(--menu-item-icon-transform);transition:transform var(--transition-duration) var(--transition-function);pointer-events:none}.menu__item:hover{--menu-item-cursor: pointer}@media(min-width: 992px){.menu__item:hover .menu__link{color:var(--color-outline)}}@media(min-width: 992px){.menu__item:hover .menu__dropdown{--menu-dropdown-events: all}}@media(min-width: 992px){.menu__item:hover .menu__dropdown-wrapper{--menu-dropdown-wrapper-transform: translateY(0)}}@media(min-width: 992px){.menu__item:hover .menu__dropdown-opener{--menu-dropdown-opener-transform: rotate(270deg)}}@media(min-width: 992px){.menu__item:hover .menu__dropdown-opener path{--menu-opener-color: var(--color-outline)}}.menu__link{font-size:18px;font-weight:var(--font-weight-bold);line-height:1.4;padding:16px var(--menu-link-indent) 16px 0;grid-area:item-1;z-index:1;position:relative}@media(min-width: 992px){.menu__link{padding:0 var(--menu-link-indent) 0 0}}.has-dropdown .menu__link{--menu-link-indent: 16px}@media(min-width: 992px){.has-dropdown .menu__link{--menu-link-indent: 10px}}.menu__dropdown{position:var(--menu-dropdown-position);top:var(--menu-dropdown-top);left:var(--menu-dropdown-left);right:var(--menu-dropdown-right);width:var(--menu-dropdown-width);height:var(--menu-dropdown-height);margin:0;transform:var(--menu-dropdown-transform);transition:transform var(--transition-duration) var(--transition-function);overflow:clip;pointer-events:var(--menu-dropdown-events);display:block !important;z-index:2}@media(min-width: 992px){.menu__dropdown-list{padding:var(--menu-dropdown-list-padding);display:var(--menu-dropdown-list-display);grid-template-columns:repeat(auto-fill, minmax(280px, 1fr));gap:var(--menu-dropdown-list-gap)}}@media(min-width: 992px){.menu__dropdown-list .menu__dropdown-list{--menu-dropdown-list-display: block;--menu-dropdown-list-padding: 0;--menu-dropdown-list-gap: 0}}.menu__dropdown-item{display:var(--menu-dropdown-item-display);grid-template-columns:1fr min-content;grid-template-areas:"drop-1 item-2";align-items:center;margin:var(--menu-dropdown-item-margin);padding:0}@media(min-width: 992px){.menu__dropdown-item .menu__dropdown-item{--menu-dropdown-item-margin: 8px 0}}@media(min-width: 992px){.menu__dropdown-item .menu__dropdown-item .menu__dropdown-link{--menu-dropdown-link-font-size: 16px;--menu-dropdown-link-font-weight: var(--font-weight-medium)}}.menu__dropdown-link{display:var(--menu-dropdown-link-display);align-items:center;justify-content:space-between;grid-area:drop-1;padding:var(--menu-dropdown-link-padding);font-size:var(--menu-dropdown-link-font-size);line-height:1.4;font-weight:var(--menu-dropdown-link-font-weight)}@media(min-width: 992px){.menu__dropdown-link{vertical-align:top}}.has-dropdown .menu__dropdown-link{--menu-dropdown-link-indent: 16px}.menu__dropdown-opener{height:var(--menu-dropdown-opener-height);width:var(--menu-dropdown-opener-width);max-width:1920px;display:var(--menu-dropdown-opener-display);align-items:center;justify-content:var(--menu-dropdown-opener-position);font-weight:var(--menu-dropdown-opener-weight);cursor:pointer;transition:all var(--transition-duration) var(--transition-function);font-size:18px;grid-area:item-2;position:relative;margin:0 auto;z-index:1;color:var(--menu-opener-color)}@media(min-width: 992px){.menu__dropdown-opener{transform:var(--menu-dropdown-opener-transform)}}.menu__dropdown-opener:hover{--menu-opener-color: var(--color-outline)}.menu__dropdown-opener path:hover{--menu-opener-color: var(--color-outline)}.menu__dropdown-wrapper{position:var(--menu-dropdown-wrapper-position);top:0;left:0;width:var(--menu-dropdown-wrapper-width);height:var(--menu-dropdown-wrapper-height);max-height:calc(100vh - var(--header-height, 65px));max-width:1920px;margin:var(--menu-dropdown-wrapper-margin);padding:var(--menu-dropdown-wrapper-padding);background:var(--background-primary, #fff);overflow:var(--menu-dropdown-wrapper-overflow);transform:var(--menu-dropdown-wrapper-transform);transition:transform var(--transition-duration) var(--transition-function);border-bottom:1px solid color-mix(in srgb, var(--color-primary) 15%, transparent)}.menu__dropdown .menu__dropdown{--menu-dropdown-transform: translateX(100%)}@media(min-width: 992px){.menu__dropdown .menu__dropdown{--menu-dropdown-position: static;--menu-dropdown-transform: translateX(0);--menu-dropdown-width: 100%}}@media(min-width: 992px){.menu__dropdown .menu__dropdown-wrapper .menu__dropdown-wrapper{--menu-dropdown-wrapper-transform: none;border-color:rgba(0,0,0,0)}}@media(min-width: 992px){.has-dropdown .menu__dropdown .menu__dropdown-wrapper .menu__dropdown-link{--menu-dropdown-link-indent: 0}}@media(min-width: 992px){.menu__dropdown .menu__dropdown-wrapper .menu__dropdown-opener{--menu-dropdown-opener-display: none}}.menu__dropdown>.menu__dropdown-wrapper>.menu__dropdown-opener{--menu-dropdown-opener-position: flex-start;--menu-dropdown-opener-weight: var(--font-weight-semibold);--menu-dropdown-opener-width: auto}.menu__dropdown>.menu__dropdown-wrapper>.menu__dropdown-opener .menu__item-icon{--menu-item-icon-transform: rotate(90deg);--menu-item-icon-margin: 0 10px -1px 0}[id^=header-dropdown-trigger-menu]:checked~.menu__dropdown>.menu__dropdown-wrapper,[id*=header-dropdown-trigger-menu]:checked~.menu__dropdown>.menu__dropdown-wrapper{--menu-dropdown-wrapper-transform: translateX(0)}[id^=header-dropdown-trigger-childmenu]:checked~.menu__dropdown>.menu__dropdown-wrapper,[id*=header-dropdown-trigger-childmenu]:checked~.menu__dropdown>.menu__dropdown-wrapper{--menu-dropdown-wrapper-transform: translateX(0);--menu-dropdown-wrapper-overflow: clip auto}[id^=header-dropdown-trigger-childmenu]:checked~.menu__dropdown,[id*=header-dropdown-trigger-childmenu]:checked~.menu__dropdown,[id^=header-dropdown-trigger-menu]:checked~.menu__dropdown,[id*=header-dropdown-trigger-menu]:checked~.menu__dropdown{--menu-dropdown-transform: translateX(0);--menu-dropdown-events: all}.menu:has([id^=header-dropdown-trigger-menu]:checked) .menu__nav,.menu:has([id*=header-dropdown-trigger-menu]:checked) .menu__nav{--menu-nav-overflow: clip}.menu:has([id^=header-dropdown-trigger-childmenu]:checked) .menu__item>.menu__dropdown>.menu__dropdown-wrapper,.menu:has([id*=header-dropdown-trigger-childmenu]:checked) .menu__item>.menu__dropdown>.menu__dropdown-wrapper{--menu-dropdown-wrapper-overflow: clip}.menu .header__btn-wrapper{display:var(--menu-display)}@media(min-width: 992px){.menu .header__btn-wrapper{--menu-display: none}}@media(min-width: 992px){.menu .social-networks{display:none}}.search{--search-dropdown-delay: 50ms;--search-dropdown-duration: var(--transition-duration);--search-dropdown-events: none;--search-dropdown-padding: 16px;--search-dropdown-transform: translateY(-100%);--search-dropdown-visibility: hidden;--search-input-width: 100%;--search-icon-color: currentcolor;--search-icon-display: block;--search-icon-label-color: currentcolor;--search-icon-form-width: 100%;--search-icon-rotate: 45deg;--search-input-border-color: color-mix(in srgb, var(--color-primary) 15%, transparent);--search-input-font-size: inherit;--search-input-min-width: 230px;--search-input-padding-left: 46px;--search-input-padding-right: 55px}@media(min-width: 992px){.search{--search-dropdown-padding: 80px;--search-icon-form-width: auto}}.search g{pointer-events:none;stroke:var(--search-icon-color);transition:stroke var(--transition-duration) var(--transition-function)}.search__opener{height:var(--button-height);width:var(--button-width);border-radius:var(--button-border-radius);display:flex;align-items:center;justify-content:center;background:color-mix(in srgb, var(--color-primary) 15%, transparent);position:relative;cursor:pointer}.search__opener svg{pointer-events:none}.search__opener:hover g{--search-icon-color: var(--color-outline)}.search__closer{text-align:center;margin-top:16px;display:flex;align-items:center;justify-content:center;font-size:14px;cursor:pointer}.search__closer-icon{position:relative;display:block;width:20px;height:20px}.search__closer-icon:not(:only-child){margin-left:8px}.search__closer-icon::before,.search__closer-icon::after{content:"";position:absolute;top:50%;left:50%;height:1px;width:12px;background:var(--search-icon-label-color);transform:translate(-50%, -50%) rotate(var(--search-icon-rotate));transition:background var(--transition-duration) var(--transition-function)}.search__closer-icon::after{--search-icon-rotate: -45deg}.search__closer-label{color:var(--search-icon-label-color);transition:color var(--transition-duration) var(--transition-function)}.search__closer:hover .search__closer-label{--search-icon-label-color: var(--color-outline)}.search__closer:hover .search__closer-icon::before,.search__closer:hover .search__closer-icon::after{--search-icon-label-color: var(--color-outline)}.search__wrapper{position:absolute;width:100vw;height:auto;top:0;left:0;max-width:1920px;margin:0 auto;transform:var(--search-dropdown-transform);transition:transform var(--transition-duration) var(--transition-function),height var(--transition-duration) var(--transition-function);display:flex;flex-direction:column;align-items:center;overflow:clip;color:currentcolor;background:var(--background-primary, #fff);padding:16px var(--search-dropdown-padding);flex:0 1 auto}.search__wrapper form{position:relative;width:var(--search-icon-form-width)}.search__holder{position:absolute;height:calc(100dvh - var(--header-height, 65px));width:100%;top:var(--header-height, 65px);left:0;overflow:clip;pointer-events:var(--search-dropdown-events);visibility:var(--search-dropdown-visibility);transition:visibility var(--search-dropdown-duration) var(--transition-function) var(--search-dropdown-delay);display:flex !important}#header-opener-search:checked~.search__holder{--search-dropdown-events: all;--search-dropdown-duration: 0s;--search-dropdown-delay: 0s;--search-dropdown-visibility: visible}#header-opener-search:checked~.search__holder .search__wrapper{--search-dropdown-transform: translateY(0)}.search__reset{position:absolute;right:7px;top:50%;height:var(--button-height);width:var(--button-width);visibility:hidden;transform:translate(0, -50%);transition:visibility var(--transition-duration) var(--transition-function);display:flex;align-items:center;background:color-mix(in srgb, var(--color-primary) 15%, transparent);border-radius:var(--button-border-radius);cursor:pointer}.filled+.search__reset{visibility:visible}.search__reset::before,.search__reset::after{content:"";position:absolute;top:50%;left:50%;height:2px;width:18px;transform:translate(-50%, -50%) rotate(var(--search-icon-rotate));background:var(--search-icon-color);transition:background var(--transition-duration) var(--transition-function)}.search__reset::after{--search-icon-rotate: -45deg}.search__reset:hover::before,.search__reset:hover::after{--search-icon-color: var(--color-outline)}.search__icon{font-size:20px;width:20px;height:20px;pointer-events:none;position:absolute;top:50%;left:13px;transform:translateY(-65%);z-index:1;display:var(--search-icon-display)}.search__input{min-width:var(--search-input-min-width);width:var(--search-input-width);padding:14px var(--search-input-padding-right) 14px var(--search-input-padding-left);font-size:var(--search-input-font-size);font-weight:var(--font-weight-bold);line-height:1.25;border:1px solid var(--search-input-border-color);border-radius:var(--button-border-radius);color:currentcolor;background:var(--background-primary, #fff)}@media(min-width: 992px){.search__input{--search-input-border-color: transparent;--search-input-font-size: 20px;--search-input-min-width: auto;--search-input-padding-left: 5px;--search-input-padding-right: 60px;--search-input-width: auto}}.search__input-wrapper{position:relative;flex:1 1 auto}@media(min-width: 992px){.search__input-wrapper .search__icon{--search-icon-display: none}}.search__input::placeholder{color:color-mix(in srgb, var(--color-primary) 50%, transparent)}.search__input::-webkit-search-cancel-button{display:none !important}.search__input::-webkit-search-decoration{display:none}.search__input:focus{outline:none}.search__input:-webkit-autofill,.search__input:autofill{box-shadow:inset 0 0 0 150px var(--background-primary, #fff) !important}.search:focus-within .search__holder{--search-dropdown-delay: 0s;--search-dropdown-duration: 0s;--search-dropdown-events: all;--search-dropdown-visibility: visible}.header{--header-background: transparent;--header-border-color: color-mix(in srgb, var(--color-primary) 15%, transparent);--header-btn-display: none;--header-content-gap: 8px;--header-logo-visibility: visible;--header-position: static;--header-transition-duration: var(--transition-duration);--header-transition-delay: var(--transition-duration);z-index:1;position:var(--header-position);top:0;left:0;right:0}@media(min-width: 992px){.header{--header-btn-display: block}}.header__inner{--header-logo-grow: 1;--header-menu-grow: 0;--header-menu-bottom-padding: 0;--header-menu-indent: var(--menu-indent-mobile);--header-padding-bottom: var(--padding-bottom-mobile, 12px);--header-padding-top: var(--padding-top-mobile, 12px);--header-order-app: var(--order-app-mobile, 2);--header-order-button: var(--order-button-mobile, 6);--header-order-cart: var(--order-cart-mobile, 4);--header-order-logo: var(--order-logo-mobile, 1);--header-order-menu: var(--order-menu-mobile, 5);--header-order-search: var(--order-search-mobile, 3);color:var(--color-primary, #000);background:var(--header-background);border-bottom:1px solid var(--header-border-color);transition:background var(--header-transition-duration) var(--header-transition-delay) var(--transition-function),border var(--header-transition-duration) var(--header-transition-delay) var(--transition-function),padding var(--transition-duration) var(--transition-function)}@media(min-width: 992px){.header__inner{--header-logo-grow: var(--logo-growth-desktop, 1);--header-menu-grow: var(--menu-growth-desktop, 1);--header-menu-bottom-padding: var(--menu-bottom-padding-top-desktop);--header-menu-bottom-border-top-width: var(--menu-bottom-border-top-width-desktop, 100%);--header-menu-indent: var(--menu-indent-desktop);--header-padding-bottom: var(--padding-bottom-desktop, 12px);--header-padding-top: var(--padding-top-desktop, 12px);--header-order-app: var(--order-app-desktop, 2);--header-order-button: var(--order-button-desktop, 5);--header-order-cart: var(--order-cart-desktop, 4);--header-order-logo: var(--order-logo-desktop, 1);--header-order-menu: var(--order-menu-desktop, 6);--header-order-search: var(--order-search-desktop, 3)}}.header__inner--padding-top{padding-top:var(--header-padding-top)}.header__inner--padding-bottom{padding-bottom:var(--header-padding-bottom)}.header__inner--background{background:var(--background-primary, #fff)}.header__content{display:flex;justify-content:space-between;align-items:stretch}@media(min-width: 992px){.header__content{flex-wrap:wrap}}@media(min-width: 992px){.header__content--outlined{border:1px solid color-mix(in srgb, var(--color-primary) 15%, transparent);border-radius:var(--border-radius);padding:var(--header-padding-top) var(--header-padding-bottom) var(--header-padding-bottom);margin:calc(-1*var(--header-padding-top)) 0 calc(-1*var(--header-padding-bottom))}}.header__menu{flex:var(--header-menu-grow) 1 auto;order:var(--header-order-menu);margin:var(--header-menu-indent)}@media(min-width: 992px){.header__menu{width:var(--menu-bottom-width-desktop, 100%);position:relative;padding:var(--header-menu-bottom-padding)}}@media(min-width: 992px){.header__menu::before{content:"";display:block;width:calc(var(--header-menu-bottom-border-top-width));height:var(--menu-border-width-desktop, 1px);margin:0;position:absolute;left:50%;top:0;transform:translateX(-50%);background:color-mix(in srgb, var(--color-primary) 15%, transparent)}}.header__logo{flex:var(--header-logo-grow) 1 auto;order:var(--header-order-logo);padding-right:16px}.header__logo-image-holder{transition:all var(--header-transition-duration) var(--header-transition-delay) var(--transition-function);visibility:var(--header-logo-visibility)}.header__search{flex:0 1 auto;order:var(--header-order-search);padding-left:var(--header-content-gap)}.header__cart{order:var(--header-order-cart);flex:0 1 auto;padding-left:var(--header-content-gap)}.header__btn-wrapper{flex:0 1 auto;display:var(--header-btn-display)}@media(min-width: 992px){.header__btn-wrapper{order:var(--header-order-button);padding-left:var(--header-content-gap)}}.header:has(.header__inner--sticky){--header-position: sticky}.header:has(.header__content--outlined){--header-border-color: transparent}.header:not(.header:has(.header__inner--background)){--header-border-color: transparent;--header-position: absolute}.header:not(.header:has(.header__inner--background)) .header__logo-image-holder{--header-logo-visibility: hidden}.header:not(.header:has(.header__inner--background)) .header__logo-image-holder--floated{--header-logo-visibility: visible}.header:not(.header:has(.header__inner--background)) .header__logo-image-holder:only-child{--header-logo-visibility: visible}.header:not(.header:has(.header__inner--background)):has(.header__inner--sticky){--header-position: fixed}.header:has(.menu__item.has-dropdown:hover),.header:has(#header-opener-menu:checked),.header:has(#header-opener-search:checked){--header-border-color: color-mix(in srgb, var(--color-primary) 15%, transparent);--header-transition-duration: 0s;--header-transition-delay: 0s}.header:has(.menu__item.has-dropdown:hover) .header__inner,.header:has(#header-opener-menu:checked) .header__inner,.header:has(#header-opener-search:checked) .header__inner{--header-background: var(--background-primary, #fff);--header-transition-duration: 0s;--header-transition-delay: 0s}.header:has(.menu__item.has-dropdown:hover) .header__inner .header__logo-image-holder,.header:has(#header-opener-menu:checked) .header__inner .header__logo-image-holder,.header:has(#header-opener-search:checked) .header__inner .header__logo-image-holder{--header-logo-visibility: visible}.header:has(.menu__item.has-dropdown:hover) .header__inner .header__logo-image-holder--floated,.header:has(#header-opener-menu:checked) .header__inner .header__logo-image-holder--floated,.header:has(#header-opener-search:checked) .header__inner .header__logo-image-holder--floated{--header-logo-visibility: hidden}.header:has(.menu__item.has-dropdown:hover) .header__inner .header__logo-image-holder--floated:only-child,.header:has(#header-opener-menu:checked) .header__inner .header__logo-image-holder--floated:only-child,.header:has(#header-opener-search:checked) .header__inner .header__logo-image-holder--floated:only-child{--header-logo-visibility: visible}@media(min-width: 992px){.header:has(.menu__item.has-dropdown:hover):has(.header__content--outlined),.header:has(#header-opener-menu:checked):has(.header__content--outlined),.header:has(#header-opener-search:checked):has(.header__content--outlined){--header-border-color: transparent}} diff --git a/assets/main.js b/assets/main.js new file mode 100644 index 0000000..c48f458 --- /dev/null +++ b/assets/main.js @@ -0,0 +1 @@ +import{frameSequence as o,onReady as d}from"utils.js";const e={selector:{o:document.documentElement},i:{loaded:"loaded"}},s={t(){o(()=>({o:e.selector.o,i:e.i.loaded}),o=>{o.o.classList.add(o.i)})}},i=()=>{s.t()};d(i); \ No newline at end of file diff --git a/assets/styles.css b/assets/styles.css index 7df1595..f53b8f9 100644 --- a/assets/styles.css +++ b/assets/styles.css @@ -1 +1 @@ -#main>section:last-child{display:flex;flex-direction:column;flex-grow:1}#main>section:last-child>[class*=__wrapper],#main>section:last-child>[class^=__wrapper]{flex-grow:1}.preview-app #main:has(section:only-child){display:block}.bq-content.rx-content{font-family:var(--font-body);line-height:var(--font-height-lg);font-size:inherit}.bq-content.rx-content b,.bq-content.rx-content strong{font-weight:var(--font-weight-semibold)}.bq-content.rx-content a{color:var(--color-outline);transition:color var(--transition-duration) ease-in-out}h1 .bq-content.rx-content a,h2 .bq-content.rx-content a,h3 .bq-content.rx-content a,h4 .bq-content.rx-content a,h5 .bq-content.rx-content a,h6 .bq-content.rx-content a{text-decoration:none;color:var(--color-outline)}.bq-content.rx-content a:hover{opacity:.8}h1 .bq-content.rx-content a:hover,h2 .bq-content.rx-content a:hover,h3 .bq-content.rx-content a:hover,h4 .bq-content.rx-content a:hover,h5 .bq-content.rx-content a:hover,h6 .bq-content.rx-content a:hover{opacity:.8}.bq-content.rx-content ul{list-style:disc;margin-left:26px}.bq-content.rx-content ul+*{margin-top:16px}.bq-content.rx-content ol{list-style:decimal;margin-left:26px}.bq-content.rx-content ol+*{margin-top:16px}.bq-content.rx-content li{font-size:16px;line-height:var(--font-height-lg)}.bq-content.rx-content li li{font-size:1em}.bq-content.rx-content h1,.bq-content.rx-content h2,.bq-content.rx-content h3,.bq-content.rx-content h4,.bq-content.rx-content h5,.bq-content.rx-content h6{font-family:var(--font-heading);font-size:16px;font-weight:var(--font-weight-headers);line-height:var(--font-height-lg);text-rendering:optimizelegibility;letter-spacing:0;color:var(--color-primary, #000)}.bq-content.rx-content h1+*,.bq-content.rx-content h2+*,.bq-content.rx-content h3+*,.bq-content.rx-content h4+*,.bq-content.rx-content h5+*,.bq-content.rx-content h6+*{margin-top:12px}.bq-content.rx-content h1{font-size:var(--font-size-h1);font-weight:var(--font-weight-h1);line-height:var(--font-height-sm)}.bq-content.rx-content h1 b,.bq-content.rx-content h1 strong{font-family:inherit;font-weight:calc(var(--font-weight-h1) + 200)}.bq-content.rx-content h1:last-child{margin-bottom:0}.bq-content.rx-content h2{font-size:var(--font-size-h2);font-weight:var(--font-weight-h2);line-height:var(--font-height-sm)}.bq-content.rx-content h2 b,.bq-content.rx-content h2 strong{font-family:inherit;font-weight:calc(var(--font-weight-h2) + 200)}h1+.bq-content.rx-content h2,p+.bq-content.rx-content h2,dl+.bq-content.rx-content h2,pre+.bq-content.rx-content h2,address+.bq-content.rx-content h2,table+.bq-content.rx-content h2,blockquote+.bq-content.rx-content h2,ol+.bq-content.rx-content h2,ul+.bq-content.rx-content h2{margin-top:24px}.bq-content.rx-content h2:last-child{margin-bottom:0}.bq-content.rx-content h3{font-size:var(--font-size-h3);font-weight:var(--font-weight-h3);line-height:var(--font-height-sm)}.bq-content.rx-content h3 b,.bq-content.rx-content h3 strong{font-family:inherit;font-weight:calc(var(--font-weight-h3) + 200)}h2+.bq-content.rx-content h3,p+.bq-content.rx-content h3,dl+.bq-content.rx-content h3,pre+.bq-content.rx-content h3,address+.bq-content.rx-content h3,table+.bq-content.rx-content h3,blockquote+.bq-content.rx-content h3,ol+.bq-content.rx-content h3,ul+.bq-content.rx-content h3{margin-top:24px}.bq-content.rx-content h3:last-child{margin-bottom:0}h3+.bq-content.rx-content h4,p+.bq-content.rx-content h4,dl+.bq-content.rx-content h4,pre+.bq-content.rx-content h4,address+.bq-content.rx-content h4,table+.bq-content.rx-content h4,blockquote+.bq-content.rx-content h4,ol+.bq-content.rx-content h4,ul+.bq-content.rx-content h4{margin-top:24px}h4+.bq-content.rx-content h5,p+.bq-content.rx-content h5,dl+.bq-content.rx-content h5,pre+.bq-content.rx-content h5,address+.bq-content.rx-content h5,table+.bq-content.rx-content h5,blockquote+.bq-content.rx-content h5,ol+.bq-content.rx-content h5,ul+.bq-content.rx-content h5{margin-top:24px}h5+.bq-content.rx-content h6,p+.bq-content.rx-content h6,dl+.bq-content.rx-content h6,pre+.bq-content.rx-content h6,address+.bq-content.rx-content h6,table+.bq-content.rx-content h6,blockquote+.bq-content.rx-content h6,ol+.bq-content.rx-content h6,ul+.bq-content.rx-content h6{margin-top:24px}.bq-content.rx-content h4 b,.bq-content.rx-content h4 strong,.bq-content.rx-content h5 b,.bq-content.rx-content h5 strong,.bq-content.rx-content h6 b,.bq-content.rx-content h6 strong{font-family:inherit;font-weight:calc(var(--font-weight-headers) + 200)}.bq-content.rx-content table{border-collapse:collapse;border-spacing:0;width:100%;empty-cells:show}.bq-content.rx-content table+*{margin-top:16px}.bq-content.rx-content td,.bq-content.rx-content th{text-align:left;vertical-align:top;font-size:95%;line-height:var(--font-height-lg);padding:.75em;border:1px solid var(--color-border)}.bq-content.rx-content th{font-weight:normal}thead .bq-content.rx-content th{font-weight:var(--font-weight-bold)}.bq-content.rx-content dl+*{margin-top:16px}.bq-content.rx-content dt{font-weight:var(--font-weight-bold);font-size:16px;line-height:var(--font-height-lg)}.bq-content.rx-content dd{font-size:16px;line-height:var(--font-height-lg)}.bq-content.rx-content dd+dt{margin-top:.25em}.bq-content.rx-content address{font-style:normal}.bq-content.rx-content address+*{margin-top:16px}.bq-content.rx-content dfn,.bq-content.rx-content abbr,.bq-content.rx-content time,.bq-content.rx-content small,.bq-content.rx-content var,.bq-content.rx-content samp,.bq-content.rx-content kbd,.bq-content.rx-content code{font-size:16px}.bq-content.rx-content dfn[title],.bq-content.rx-content abbr[title]{letter-spacing:.01em;text-transform:uppercase;text-decoration:none;cursor:help;border-bottom:1px dotted var(--color-border);border-top-color:var(--color-border)}.bq-content.rx-content sup,.bq-content.rx-content sub{font-size:75%;line-height:0;margin-left:2px}.bq-content.rx-content sub{vertical-align:sub}.bq-content.rx-content sup{vertical-align:super}.bq-content.rx-content var,.bq-content.rx-content code,.bq-content.rx-content kbd,.bq-content.rx-content samp{display:inline-block;font-family:SFMono-Regular,Consolas,"Liberation Mono",Menlo,Courier,monospace;font-style:normal;line-height:1;vertical-align:baseline}.bq-content.rx-content samp,.bq-content.rx-content kbd,.bq-content.rx-content code{border-radius:var(--border-radius)}.bq-content.rx-content kbd{padding:.2em .4em;border:1px solid var(--color-border)}.bq-content.rx-content code{position:relative;top:-1px;border:1px solid rgba(0,0,0,0);padding:.2em .4em}.bq-content.rx-content p{font-size:inherit;line-height:var(--font-height-lg)}.bq-content.rx-content p+*{margin-top:16px}.bq-content.rx-content cite{font-style:italic;font-size:20px;font-weight:var(--font-weight-semibold)}.bq-content.rx-content cite small,.bq-content.rx-content cite b,.bq-content.rx-content cite strong{font-style:normal}.bq-content.rx-content blockquote{font-style:normal;font-weight:normal;padding:0;background:rgba(0,0,0,0)}.bq-content.rx-content blockquote p{font-size:125%;line-height:var(--font-height-lg)}.bq-content.rx-content blockquote p+p{margin-top:4px}.bq-content.rx-content blockquote cite{text-align:left;font-style:normal}.bq-content.rx-content blockquote+*{margin-top:16px}.bq-content.rx-content caption{text-align:left;font-style:normal}.bq-content.rx-content hr{position:relative;line-height:1;background:none;border:none;text-align:left;margin-top:1em;margin-bottom:1em;height:10px}.bq-content.rx-content hr::before{content:"";position:absolute;top:50%;width:100%;max-width:100%;margin-top:-0.5px;border-top-width:1px;border-top-style:solid}.bq-content.rx-content div+*{margin-top:24px}.bq-content.rx-content pre{overflow:auto;white-space:pre;font-family:SFMono-Regular,Consolas,"Liberation Mono",Menlo,Courier,monospace;font-size:90%;line-height:var(--font-height-lg);padding:1em;background:var(--color-placeholder)}.bq-content.rx-content pre code{padding:0;border:0;background:none;font-size:100%;display:block;line-height:inherit}.bq-content.rx-content pre+*{margin-top:16px}.bq-content.rx-content .embed-responsive{position:relative;padding:0;margin:0;padding-bottom:56.25%;padding-top:25px;height:0}.btn{font-family:var(--font-body);font-size:var(--font-size-button);font-weight:var(--font-weight-button);padding:var(--padding-button);margin:0;color:currentcolor;background:none;text-decoration:none;border:1px solid rgba(0,0,0,0);border-radius:var(--border-radius-button);cursor:pointer;display:inline-flex;justify-content:center;align-items:center;line-height:var(--font-height-sm);transition:all var(--transition-duration) ease-in-out}.btn:hover{opacity:.8}.btn--primary{border-color:var(--background-accent);background-color:var(--background-accent);color:var(--color-accent)}.btn--outline{border-color:currentcolor;background-color:rgba(0,0,0,0);color:currentcolor} +@media(max-width: 991px){html:has(#header-opener-menu:checked),html:has(#header-opener-search:checked){overflow:clip}}body{--overlay-transition-duration: var(--transition-duration);--overlay-transition-delay: 50ms;--overlay-visibility: hidden}body::before{content:"";position:absolute;top:0;left:0;width:100vw;height:100%;z-index:-1;pointer-events:none;background:color-mix(in srgb, var(--color-primary) 55%, transparent);visibility:var(--overlay-visibility);transition:visibility var(--overlay-transition-duration) var(--overlay-transition-delay) var(--transition-function)}@media(max-width: 991px){body:has(#header-opener-menu:checked),body:has(#header-opener-search:checked){overflow:clip}}body:has(#header-opener-menu:checked)::before,body:has(#header-opener-search:checked)::before{--overlay-transition-duration: 0s;--overlay-transition-delay: 0s;--overlay-visibility: visible}body:has(.menu__item.has-dropdown:hover)::before{--overlay-transition-duration: 0s;--overlay-transition-delay: 0s;--overlay-visibility: visible}#main>section:last-child{display:flex;flex-direction:column;flex-grow:1}#main>section:last-child>[class*=__wrapper],#main>section:last-child>[class^=__wrapper]{flex-grow:1}.preview-app #main:has(section:only-child){display:block}.bq-content.rx-content{font-family:var(--font-body);line-height:1.4;font-size:inherit}.bq-content.rx-content b,.bq-content.rx-content strong{font-weight:var(--font-weight-semibold)}.bq-content.rx-content a{color:var(--color-outline);transition:color var(--transition-duration) var(--transition-function)}h1 .bq-content.rx-content a,h2 .bq-content.rx-content a,h3 .bq-content.rx-content a,h4 .bq-content.rx-content a,h5 .bq-content.rx-content a,h6 .bq-content.rx-content a{text-decoration:none;color:var(--color-outline)}.bq-content.rx-content a:hover{opacity:.8}h1 .bq-content.rx-content a:hover,h2 .bq-content.rx-content a:hover,h3 .bq-content.rx-content a:hover,h4 .bq-content.rx-content a:hover,h5 .bq-content.rx-content a:hover,h6 .bq-content.rx-content a:hover{opacity:.8}.bq-content.rx-content ul{list-style:disc;margin-left:26px}.bq-content.rx-content ul+*{margin-top:16px}.bq-content.rx-content ol{list-style:decimal;margin-left:26px}.bq-content.rx-content ol+*{margin-top:16px}.bq-content.rx-content li{font-size:16px;line-height:1.4}.bq-content.rx-content li li{font-size:1em}.bq-content.rx-content h1,.bq-content.rx-content h2,.bq-content.rx-content h3,.bq-content.rx-content h4,.bq-content.rx-content h5,.bq-content.rx-content h6{font-family:var(--font-heading);font-size:16px;font-weight:var(--font-weight-headers);line-height:1.4;text-rendering:optimizelegibility;letter-spacing:0;color:var(--color-primary, #000)}.bq-content.rx-content h1+*,.bq-content.rx-content h2+*,.bq-content.rx-content h3+*,.bq-content.rx-content h4+*,.bq-content.rx-content h5+*,.bq-content.rx-content h6+*{margin-top:12px}.bq-content.rx-content h1{font-size:var(--font-size-h1);font-weight:var(--font-weight-h1);line-height:1.25}.bq-content.rx-content h1 b,.bq-content.rx-content h1 strong{font-family:inherit;font-weight:calc(var(--font-weight-h1) + 200)}.bq-content.rx-content h1:last-child{margin-bottom:0}.bq-content.rx-content h2{font-size:var(--font-size-h2);font-weight:var(--font-weight-h2);line-height:1.25}.bq-content.rx-content h2 b,.bq-content.rx-content h2 strong{font-family:inherit;font-weight:calc(var(--font-weight-h2) + 200)}h1+.bq-content.rx-content h2,p+.bq-content.rx-content h2,dl+.bq-content.rx-content h2,pre+.bq-content.rx-content h2,address+.bq-content.rx-content h2,table+.bq-content.rx-content h2,blockquote+.bq-content.rx-content h2,ol+.bq-content.rx-content h2,ul+.bq-content.rx-content h2{margin-top:24px}.bq-content.rx-content h2:last-child{margin-bottom:0}.bq-content.rx-content h3{font-size:var(--font-size-h3);font-weight:var(--font-weight-h3);line-height:1.25}.bq-content.rx-content h3 b,.bq-content.rx-content h3 strong{font-family:inherit;font-weight:calc(var(--font-weight-h3) + 200)}h2+.bq-content.rx-content h3,p+.bq-content.rx-content h3,dl+.bq-content.rx-content h3,pre+.bq-content.rx-content h3,address+.bq-content.rx-content h3,table+.bq-content.rx-content h3,blockquote+.bq-content.rx-content h3,ol+.bq-content.rx-content h3,ul+.bq-content.rx-content h3{margin-top:24px}.bq-content.rx-content h3:last-child{margin-bottom:0}h3+.bq-content.rx-content h4,p+.bq-content.rx-content h4,dl+.bq-content.rx-content h4,pre+.bq-content.rx-content h4,address+.bq-content.rx-content h4,table+.bq-content.rx-content h4,blockquote+.bq-content.rx-content h4,ol+.bq-content.rx-content h4,ul+.bq-content.rx-content h4{margin-top:24px}h4+.bq-content.rx-content h5,p+.bq-content.rx-content h5,dl+.bq-content.rx-content h5,pre+.bq-content.rx-content h5,address+.bq-content.rx-content h5,table+.bq-content.rx-content h5,blockquote+.bq-content.rx-content h5,ol+.bq-content.rx-content h5,ul+.bq-content.rx-content h5{margin-top:24px}h5+.bq-content.rx-content h6,p+.bq-content.rx-content h6,dl+.bq-content.rx-content h6,pre+.bq-content.rx-content h6,address+.bq-content.rx-content h6,table+.bq-content.rx-content h6,blockquote+.bq-content.rx-content h6,ol+.bq-content.rx-content h6,ul+.bq-content.rx-content h6{margin-top:24px}.bq-content.rx-content h4 b,.bq-content.rx-content h4 strong,.bq-content.rx-content h5 b,.bq-content.rx-content h5 strong,.bq-content.rx-content h6 b,.bq-content.rx-content h6 strong{font-family:inherit;font-weight:calc(var(--font-weight-headers) + 200)}.bq-content.rx-content table{border-collapse:collapse;border-spacing:0;width:100%;empty-cells:show}.bq-content.rx-content table+*{margin-top:16px}.bq-content.rx-content td,.bq-content.rx-content th{text-align:left;vertical-align:top;font-size:95%;line-height:1.4;padding:.75em;border:1px solid color-mix(in srgb, var(--color-primary) 15%, transparent)}.bq-content.rx-content th{font-weight:normal}thead .bq-content.rx-content th{font-weight:var(--font-weight-bold)}.bq-content.rx-content dl+*{margin-top:16px}.bq-content.rx-content dt{font-weight:var(--font-weight-bold);font-size:16px;line-height:1.4}.bq-content.rx-content dd{font-size:16px;line-height:1.4}.bq-content.rx-content dd+dt{margin-top:.25em}.bq-content.rx-content address{font-style:normal}.bq-content.rx-content address+*{margin-top:16px}.bq-content.rx-content dfn,.bq-content.rx-content abbr,.bq-content.rx-content time,.bq-content.rx-content small,.bq-content.rx-content var,.bq-content.rx-content samp,.bq-content.rx-content kbd,.bq-content.rx-content code{font-size:16px}.bq-content.rx-content dfn[title],.bq-content.rx-content abbr[title]{letter-spacing:.01em;text-transform:uppercase;text-decoration:none;cursor:help;border-bottom:1px dotted color-mix(in srgb, var(--color-primary) 15%, transparent);border-top-color:color-mix(in srgb, var(--color-primary) 15%, transparent)}.bq-content.rx-content sup,.bq-content.rx-content sub{font-size:75%;line-height:0;margin-left:2px}.bq-content.rx-content sub{vertical-align:sub}.bq-content.rx-content sup{vertical-align:super}.bq-content.rx-content var,.bq-content.rx-content code,.bq-content.rx-content kbd,.bq-content.rx-content samp{display:inline-block;font-family:SFMono-Regular,Consolas,"Liberation Mono",Menlo,Courier,monospace;font-style:normal;line-height:1;vertical-align:baseline}.bq-content.rx-content samp,.bq-content.rx-content kbd,.bq-content.rx-content code{border-radius:var(--border-radius)}.bq-content.rx-content kbd{padding:.2em .4em;border:1px solid color-mix(in srgb, var(--color-primary) 15%, transparent)}.bq-content.rx-content code{position:relative;top:-1px;border:1px solid rgba(0,0,0,0);padding:.2em .4em}.bq-content.rx-content p{font-size:inherit;line-height:1.4}.bq-content.rx-content p+*{margin-top:16px}.bq-content.rx-content cite{font-style:italic;font-size:20px;font-weight:var(--font-weight-semibold)}.bq-content.rx-content cite small,.bq-content.rx-content cite b,.bq-content.rx-content cite strong{font-style:normal}.bq-content.rx-content blockquote{font-style:normal;font-weight:normal;padding:0;background:rgba(0,0,0,0)}.bq-content.rx-content blockquote p{font-size:125%;line-height:1.4}.bq-content.rx-content blockquote p+p{margin-top:4px}.bq-content.rx-content blockquote cite{text-align:left;font-style:normal}.bq-content.rx-content blockquote+*{margin-top:16px}.bq-content.rx-content caption{text-align:left;font-style:normal}.bq-content.rx-content hr{position:relative;line-height:1;background:none;border:none;text-align:left;margin-top:1em;margin-bottom:1em;height:10px}.bq-content.rx-content hr::before{content:"";position:absolute;top:50%;width:100%;max-width:100%;margin-top:-0.5px;border-top-width:1px;border-top-style:solid}.bq-content.rx-content div+*{margin-top:24px}.bq-content.rx-content pre{overflow:auto;white-space:pre;font-family:SFMono-Regular,Consolas,"Liberation Mono",Menlo,Courier,monospace;font-size:90%;line-height:1.4;padding:1em;background:color-mix(in srgb, var(--color-primary) 50%, transparent)}.bq-content.rx-content pre code{padding:0;border:0;background:none;font-size:100%;display:block;line-height:inherit}.bq-content.rx-content pre+*{margin-top:16px}.bq-content.rx-content .embed-responsive{position:relative;padding:0;margin:0;padding-bottom:56.25%;padding-top:25px;height:0}.btn{--button-background: none;--button-border-color: transparent;--button-color: currentcolor;--button-indent: 0;--button-min-height: auto;font-family:var(--font-body);font-size:var(--font-size-button);font-weight:var(--font-weight-button);margin:0;padding:var(--button-indent);background:var(--button-background);color:var(--button-color);text-decoration:none;border:1px solid var(--button-border-color);border-radius:var(--button-border-radius);cursor:pointer;min-height:var(--button-min-height);display:inline-flex;justify-content:center;align-items:center;line-height:1;transition:all var(--transition-duration) var(--transition-function)}.btn:hover{opacity:.8}.btn--primary{--button-background: var(--background-accent);--button-color: var(--color-accent);--button-border-color: var(--background-accent);--button-indent: var(--button-padding);--button-min-height: var(--button-height)}.btn--primary:hover{--button-color: var(--color-accent)}.btn--outline{--button-background: transparent;--button-color: currentcolor;--button-border-color: currentcolor}.btn--outline:hover{--button-color: currentcolor}.social-networks{display:flex;flex-wrap:wrap;margin:0 calc(-1*var(--social-icon-spacing)) calc(-1*var(--social-icon-spacing));font-size:var(--social-size)}@media(min-width: 992px){.social-networks{justify-content:flex-end}}.social-networks__item{margin:0 var(--social-icon-spacing) var(--social-icon-spacing)}.social-networks__link:hover{opacity:1} diff --git a/assets/utils.js b/assets/utils.js new file mode 100644 index 0000000..663ca6f --- /dev/null +++ b/assets/utils.js @@ -0,0 +1 @@ +export const debounce=(e,t)=>{let n;return(...r)=>{clearTimeout(n),n=setTimeout(()=>e.apply(this,r),t)}};export const nextFrame=e=>requestAnimationFrame(e);export const frameSequence=(e,t)=>{nextFrame(()=>{const n=e();nextFrame(()=>t(n))})};export const requestIdle=(e,t={})=>{if("requestIdleCallback"in window)return requestIdleCallback(e,t);const n=t.timeout||100,r=Date.now();return setTimeout(()=>{e({didTimeout:!1,timeRemaining:()=>Math.max(0,50-(Date.now()-r))})},n)};export const resizeObserver=(e,t={})=>{const n={t:150,element:null,width:!0,...t};let r=null,o=window.innerWidth;const s=t=>nextFrame(()=>e(t)),c=n.t>0?debounce(s,n.t):s,u=e=>{(()=>{if(!n.width)return!1;const e=window.innerWidth;return e===o||(o=e,!1)})()||c(e)},i=(e=n.element)=>r||(r=new ResizeObserver(u),e?r.observe(e):r.observe(document.documentElement),r);return{init:i,destroy:()=>{r?.disconnect(),r=null},observe:e=>{r||i(),r?.observe(e)},unobserve:e=>{r?.unobserve(e)},get o(){return r}}};export const cleanupSystem=()=>{const e=new Map;return{register:(t,n)=>{if("function"==typeof n)if(e.set(t,n),window.themeCleanup){const e=window.themeCleanup;window.themeCleanup=()=>{n(),e()}}else window[`cleanup${t}`]=n},u:t=>{const n=e.get(t);n&&(n(),e.delete(t))},i:()=>{e.forEach((e,t)=>{try{e()}catch(e){}}),e.clear()},get l(){return Array.from(e.keys())}}};export const onReady=e=>{"complete"===document.readyState?requestIdle(e):document.addEventListener("readystatechange",t=>{"complete"===t.target.readyState&&requestIdle(e)},{once:!0})};export const setCssVar=(e,t,n=document.documentElement)=>{n.style.setProperty(e,t)};export const getCssVar=(e,t=document.documentElement)=>getComputedStyle(t).getPropertyValue(e).trim();export const getDimensions=e=>{if(!e)return{width:0,height:0};const t=e.getBoundingClientRect();return{width:t.width,height:t.height}}; \ No newline at end of file diff --git a/config/settings_data.json b/config/settings_data.json index 3952127..3dcd1af 100644 --- a/config/settings_data.json +++ b/config/settings_data.json @@ -1,7 +1,7 @@ { "current": { "button_corners": "square", - "button_font_size": 18, + "button_font_size": 16, "button_font_weight": "700", "button_size": "md", "color_schemes": { @@ -63,12 +63,68 @@ "font_weight_h2": "700", "font_weight_h3": "700", "font_weight_headers": "700", + "icon_style": "outline", "sections": { "header": { "type": "header", - "blocks": {}, - "block_order": [], + "blocks": { + "logo": { + "type": "logo", + "settings": { + "logo_custom_title": "{{ shop.name }}", + "logo_image": "", + "logo_image_floated": "", + "logo_svg": "", + "logo_svg_floated": "" + }, + "disabled": null + }, + "menu": { + "type": "menu", + "settings": { + "menu": "main-menu", + "menu_back_label": "Back", + "menu_alignment_horizontal": "center", + "menu_alignment_vertical": "top", + "menu_opener_position": "left" + }, + "disabled": null + }, + "search": { + "type": "search", + "settings": { + "search_closer_label": "Close", + "search_placeholder": "Search", + "show_search": true + }, + "disabled": null + }, + "button": { + "type": "button", + "settings": { + "button_label": "Contact us", + "button_url": "booqable://pages/contact", + "show_button_desktop": true, + "show_button_mobile": true + }, + "disabled": null + } + }, + "block_order": [ + "logo", + "menu", + "search", + "button" + ], "settings": { + "color_scheme": "set-1", + "header_outline": "none", + "padding_bottom_desktop": "lg", + "padding_bottom_mobile": "md", + "padding_top_desktop": "lg", + "padding_top_mobile": "md", + "show_transparency": true, + "sticky_header": true }, "disabled": null } diff --git a/config/settings_schema.json b/config/settings_schema.json index 500a2a1..4df2598 100644 --- a/config/settings_schema.json +++ b/config/settings_schema.json @@ -235,6 +235,127 @@ } ] }, + { + "name": "Social media", + "settings": [ + { + "type": "header", + "content": "Sizes" + }, + { + "type": "select", + "id": "social_icon_size", + "label": "Size", + "options": [ + { + "value": "sm", + "label": "Small" + }, + { + "value": "md", + "label": "Medium" + }, + { + "value": "lg", + "label": "Large" + } + ], + "default": "md" + }, + { + "type": "select", + "id": "social_icon_spacing", + "label": "Spacing", + "options": [ + { + "value": "sm", + "label": "Small" + }, + { + "value": "md", + "label": "Medium" + }, + { + "value": "lg", + "label": "Large" + } + ], + "default": "md" + }, + { + "type": "header", + "content": "Links" + }, + { + "type": "paragraph", + "content": "Note: Leave the field blank if you don't want to use the social network" + }, + { + "type": "text", + "id": "social_facebook", + "label": "Facebook", + "default": "https://facebook.com/", + "info": "Example: https://facebook.com/booqable" + }, + { + "type": "text", + "id": "social_instagram", + "label": "Instagram", + "default": "https://instagram.com/", + "info": "Example: https://instagram.com/booqable" + }, + { + "type": "text", + "id": "social_x", + "label": "X (ex. Twitter)", + "default": "https://x.com/", + "info": "Example: https://x.com/booqable" + }, + { + "type": "text", + "id": "social_linkedin", + "label": "Linkedin", + "default": "https://linkedin.com/", + "info": "Example: https://www.linkedin.com/company/booqable" + }, + { + "type": "text", + "id": "social_pinterest", + "label": "Pinterest", + "info": "Example: https://pinterest.com/booqable" + }, + { + "type": "text", + "id": "social_tiktok", + "label": "TikTok", + "info": "Example: https://tiktok.com/@booqable" + }, + { + "type": "text", + "id": "social_tumblr", + "label": "Tumblr", + "info": "Example: https://booqable.tumblr.com" + }, + { + "type": "text", + "id": "social_snapchat", + "label": "Snapchat", + "info": "Example: https://snapchat.com/add/booqable" + }, + { + "type": "text", + "id": "social_youtube", + "label": "YouTube", + "info": "Example: https://youtube.com/booqable" + }, + { + "type": "text", + "id": "social_vimeo", + "label": "Vimeo", + "info": "Example: https://vimeo.com/booqable" + } + ] + }, { "name": "Typography", "settings": [ diff --git a/layout/theme.liquid b/layout/theme.liquid index 7a03b6b..8929ac5 100644 --- a/layout/theme.liquid +++ b/layout/theme.liquid @@ -19,10 +19,10 @@ {%- render 'fonts', settings: settings -%} {% comment %} Load root variables {% endcomment %} - {%- render 'root-variables', settings: settings -%} + {%- render 'css-root-variables', settings: settings -%} {% comment %} Async CSS loading with resource hints {% endcomment %} - {%- assign css_files = "critical.css, styles.css" | split: ", " -%} + {%- assign css_files = "critical.css, styles.css, header.css" | split: ", " -%} {%- for css_file in css_files -%} {%- assign css_url = css_file | asset_url -%} @@ -48,7 +48,9 @@ {%- render 'page-performance-monitoring' -%} -
+
+ {% section 'header' %} +

Welcome to My Theme

@@ -65,6 +67,13 @@ {{ content_for_body }} {% comment %} Critical JavaScript files {% endcomment %} - + + diff --git a/package-lock.json b/package-lock.json index d897db2..abf4838 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,13 +8,15 @@ "name": "small-theme", "version": "1.0.0", "devDependencies": { - "@eslint/js": "^9.0.0", + "@eslint/js": "^9.28.0", "cpx": "^1.5.0", - "eslint": "^9.0.0", + "eslint": "^9.28.0", "imagemin-cli": "^7.0.0", "sass": "^1.77.0", "stylelint": "^16.0.0", - "stylelint-config-standard-scss": "^13.0.0" + "stylelint-config-standard-scss": "^13.0.0", + "svgo": "^3.0.2", + "terser": "^5.19.0" } }, "node_modules/@babel/code-frame": { @@ -400,6 +402,56 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", + "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, "node_modules/@keyv/serialize": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@keyv/serialize/-/serialize-1.1.0.tgz", @@ -779,7 +831,6 @@ "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", "dev": true, "license": "ISC", - "optional": true, "engines": { "node": ">=10.13.0" } @@ -1658,8 +1709,7 @@ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", "dev": true, - "license": "ISC", - "optional": true + "license": "ISC" }, "node_modules/brace-expansion": { "version": "1.1.12", @@ -1749,6 +1799,13 @@ "license": "MIT", "optional": true }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, "node_modules/cache-base": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", @@ -2111,8 +2168,7 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true, - "license": "MIT", - "optional": true + "license": "MIT" }, "node_modules/component-emitter": { "version": "1.3.1", @@ -2631,17 +2687,16 @@ } }, "node_modules/css-select": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", - "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", "dev": true, "license": "BSD-2-Clause", - "optional": true, "dependencies": { "boolbase": "^1.0.0", - "css-what": "^6.0.1", - "domhandler": "^4.3.1", - "domutils": "^2.8.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", "nth-check": "^2.0.1" }, "funding": { @@ -2649,18 +2704,17 @@ } }, "node_modules/css-tree": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", - "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", "dev": true, "license": "MIT", - "optional": true, "dependencies": { - "mdn-data": "2.0.14", - "source-map": "^0.6.1" + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" }, "engines": { - "node": ">=8.0.0" + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" } }, "node_modules/css-what": { @@ -2669,7 +2723,6 @@ "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", "dev": true, "license": "BSD-2-Clause", - "optional": true, "engines": { "node": ">= 6" }, @@ -2691,19 +2744,41 @@ } }, "node_modules/csso": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", - "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", + "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", "dev": true, "license": "MIT", - "optional": true, "dependencies": { - "css-tree": "^1.1.2" + "css-tree": "~2.2.0" }, "engines": { - "node": ">=8.0.0" + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" } }, + "node_modules/csso/node_modules/css-tree": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", + "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.28", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/mdn-data": { + "version": "2.0.28", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", + "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", + "dev": true, + "license": "CC0-1.0" + }, "node_modules/currently-unhandled": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", @@ -3052,16 +3127,15 @@ } }, "node_modules/dom-serializer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", - "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", "dev": true, "license": "MIT", - "optional": true, "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" }, "funding": { "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" @@ -3078,18 +3152,16 @@ "url": "https://github.com/sponsors/fb55" } ], - "license": "BSD-2-Clause", - "optional": true + "license": "BSD-2-Clause" }, "node_modules/domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", "dev": true, "license": "BSD-2-Clause", - "optional": true, "dependencies": { - "domelementtype": "^2.2.0" + "domelementtype": "^2.3.0" }, "engines": { "node": ">= 4" @@ -3099,16 +3171,15 @@ } }, "node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", "dev": true, "license": "BSD-2-Clause", - "optional": true, "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" }, "funding": { "url": "https://github.com/fb55/domutils?sponsor=1" @@ -3221,12 +3292,14 @@ } }, "node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", "dev": true, "license": "BSD-2-Clause", - "optional": true, + "engines": { + "node": ">=0.12" + }, "funding": { "url": "https://github.com/fb55/entities?sponsor=1" } @@ -5232,6 +5305,155 @@ "url": "https://github.com/sindresorhus/imagemin-svgo?sponsor=1" } }, + "node_modules/imagemin-svgo/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/imagemin-svgo/node_modules/css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/imagemin-svgo/node_modules/css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/imagemin-svgo/node_modules/csso": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", + "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "css-tree": "^1.1.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/imagemin-svgo/node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/imagemin-svgo/node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/imagemin-svgo/node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/imagemin-svgo/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/imagemin-svgo/node_modules/mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", + "dev": true, + "license": "CC0-1.0", + "optional": true + }, + "node_modules/imagemin-svgo/node_modules/svgo": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", + "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^4.1.3", + "css-tree": "^1.1.3", + "csso": "^4.2.0", + "picocolors": "^1.0.0", + "stable": "^0.1.8" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/immutable": { "version": "5.1.3", "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.3.tgz", @@ -6459,12 +6681,11 @@ } }, "node_modules/mdn-data": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", - "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", + "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", "dev": true, - "license": "CC0-1.0", - "optional": true + "license": "CC0-1.0" }, "node_modules/meow": { "version": "10.1.5", @@ -6884,7 +7105,6 @@ "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", "dev": true, "license": "BSD-2-Clause", - "optional": true, "dependencies": { "boolbase": "^1.0.0" }, @@ -8496,7 +8716,6 @@ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, "license": "BSD-3-Clause", - "optional": true, "engines": { "node": ">=0.10.0" } @@ -8526,6 +8745,17 @@ "urix": "^0.1.0" } }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, "node_modules/source-map-url": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", @@ -9341,26 +9571,29 @@ "dev": true }, "node_modules/svgo": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", - "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.2.tgz", + "integrity": "sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==", "dev": true, "license": "MIT", - "optional": true, "dependencies": { "@trysound/sax": "0.2.0", "commander": "^7.2.0", - "css-select": "^4.1.3", - "css-tree": "^1.1.3", - "csso": "^4.2.0", - "picocolors": "^1.0.0", - "stable": "^0.1.8" + "css-select": "^5.1.0", + "css-tree": "^2.3.1", + "css-what": "^6.1.0", + "csso": "^5.0.5", + "picocolors": "^1.0.0" }, "bin": { "svgo": "bin/svgo" }, "engines": { - "node": ">=10.13.0" + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/svgo" } }, "node_modules/svgo/node_modules/commander": { @@ -9369,7 +9602,6 @@ "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", "dev": true, "license": "MIT", - "optional": true, "engines": { "node": ">= 10" } @@ -9517,6 +9749,25 @@ "node": ">=4" } }, + "node_modules/terser": { + "version": "5.44.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.44.0.tgz", + "integrity": "sha512-nIVck8DK+GM/0Frwd+nIhZ84pR/BX7rmXMfYwyg+Sri5oGVE99/E3KvXqpC2xHFxyqXyGHTKBSioxxplrO4I4w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.15.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", diff --git a/package.json b/package.json index 93a9c6f..e1f6ad9 100644 --- a/package.json +++ b/package.json @@ -5,25 +5,29 @@ "private": true, "scripts": { "build:scss": "sass src/scss:assets --style=compressed --no-source-map", - "build:js": "cpx 'src/js/**/*' assets", + "build:js": "npm run minify:js && npm run copy:js:utils", + "minify:js": "find src/js -name '*.js' -not -path '*/utils/*' -exec sh -c 'terser \"$1\" --compress passes=3,drop_console=true,pure_funcs=[console.log,console.info] --mangle toplevel --mangle-props regex=/./ --output \"assets/$(basename \"$1\")\"' _ {} \\;", + "copy:js:utils": "cpx 'src/js/utils/**/*' assets/utils", "build:images": "imagemin 'src/images/**/*' --out-dir=assets", + "build:svg": "node src/svg/_utils/optimize-svg.js", "build:liquid": "npm run copy:config && npm run copy:layout && npm run copy:sections && npm run copy:snippets && npm run copy:templates", "copy:config": "cpx 'src/config/**/*' config", "copy:layout": "cpx 'src/layout/**/*' layout", "copy:sections": "cpx 'src/sections/**/*' sections", - "copy:snippets": "cpx 'src/snippets/**/*' snippets", + "copy:snippets": "find src/snippets -name '*.liquid' -not -path '*/_*' -exec cp {} snippets/ \\;", "copy:templates": "cpx 'src/templates/**/*' templates", - "build": "npm run build:scss && npm run build:js && npm run build:images && npm run build:liquid", + "build": "npm run build:scss && npm run build:js && npm run build:images && npm run build:svg && npm run build:liquid", "watch:scss": "sass src/scss:assets --watch --style=compressed --no-source-map", "watch:js": "cpx 'src/js/**/*' assets --watch", "watch:images": "cpx 'src/images/**/*' assets --watch", + "watch:svg": "npm run build:svg", "watch:liquid": "npm run watch:config & npm run watch:layout & npm run watch:sections & npm run watch:snippets & npm run watch:templates", "watch:config": "cpx 'src/config/**/*' config --watch", "watch:layout": "cpx 'src/layout/**/*' layout --watch", "watch:sections": "cpx 'src/sections/**/*' sections --watch", "watch:snippets": "cpx 'src/snippets/**/*' snippets --watch", "watch:templates": "cpx 'src/templates/**/*' templates --watch", - "watch": "npm run watch:scss & npm run watch:js & npm run watch:images & npm run watch:liquid", + "watch": "npm run watch:scss & npm run watch:js & npm run watch:images & npm run watch:svg & npm run watch:liquid", "dev": "npm run build && npm run watch", "clean": "rm -rf assets/* config/* layout/* sections/* snippets/* templates/*", "clean:selective": "npm run build:liquid && npm run build:js && npm run build:images && npm run build:scss", @@ -34,6 +38,8 @@ }, "devDependencies": { "sass": "^1.77.0", + "terser": "^5.19.0", + "svgo": "^3.0.2", "cpx": "^1.5.0", "imagemin-cli": "^7.0.0", "stylelint": "^16.0.0", diff --git a/sections/header.liquid b/sections/header.liquid new file mode 100644 index 0000000..7a97f6c --- /dev/null +++ b/sections/header.liquid @@ -0,0 +1,581 @@ +{%- assign app = section.blocks | where: "type", "app" | first -%} +{%- assign block_id = '' -%} +{%- assign color_scheme = section.settings.color_scheme -%} +{%- assign header_outline = section.settings.header_outline -%} +{%- assign menu_alignment_horizontal = '' -%} +{%- assign menu_alignment_vertical = '' -%} +{%- assign menu_opener_position = '' -%} +{%- assign padding_bottom_desktop = section.settings.padding_bottom_desktop -%} +{%- assign padding_bottom_mobile = section.settings.padding_bottom_mobile -%} +{%- assign padding_top_desktop = section.settings.padding_top_desktop -%} +{%- assign padding_top_mobile = section.settings.padding_top_mobile -%} +{%- assign section_type = section.type -%} +{%- assign show_button_desktop = '' -%} +{%- assign show_button_mobile = '' -%} +{%- assign show_transparency = section.settings.show_transparency -%} +{%- assign sticky_header = section.settings.sticky_header -%} + +{%- for block in section.blocks -%} + {%- case block.type -%} + {%- when 'menu' -%} + {%- assign block_id = block.id -%} + {%- assign menu_alignment_horizontal = block.settings.menu_alignment_horizontal -%} + {%- assign menu_alignment_vertical = block.settings.menu_alignment_vertical -%} + {%- assign menu_opener_position = block.settings.menu_opener_position -%} + + {%- when 'button' -%} + {%- assign button_label = block.settings.button_label -%} + {%- assign button_url = block.settings.button_url -%} + {%- assign show_button_desktop = block.settings.show_button_desktop -%} + {%- assign show_button_mobile = block.settings.show_button_mobile -%} + + {%- if button_label != blank and button_url != blank -%} + {%- capture element_button -%} + {%- render 'button' + button_label: button_label, + button_type: 'primary', + button_url: button_url, + section_type: section_type + -%} + {%- endcapture -%} + {%- endif -%} + {%- endcase -%} +{%- endfor -%} + +{%- if app -%} + {%- capture element_app_desktop -%} +
+ {%- render 'app', block: app, section: section, size: "desktop" -%} +
+ {%- endcapture -%} +{%- endif -%} + +{% comment %} CSS variables start {% endcomment %} +{%- capture variables -%} + {%- case padding_bottom_desktop -%} + {%- when 'md' -%} + --padding-bottom-desktop: 16px; + {%- when 'lg' -%} + --padding-bottom-desktop: 20px; + {%- endcase -%} + + {%- case padding_bottom_mobile -%} + {%- when 'md' -%} + --padding-bottom-mobile: 16px; + {%- when 'lg' -%} + --padding-bottom-mobile: 20px; + {%- endcase -%} + + {%- case padding_top_desktop -%} + {%- when 'md' -%} + --padding-top-desktop: 16px; + {%- when 'lg' -%} + --padding-top-desktop: 20px; + {%- endcase -%} + + {%- case padding_top_mobile -%} + {%- when 'md' -%} + --padding-top-mobile: 16px; + {%- when 'lg' -%} + --padding-top-mobile: 20px; + {%- endcase -%} + + {%- if menu_opener_position == 'left' -%} + --menu-indent-mobile: 0 20px 0 0; + --order-app-mobile: 3; + --order-button-mobile: 6; + --order-cart-mobile: 5; + --order-logo-mobile: 2; + --order-menu-mobile: 1; + --order-search-mobile: 4; + {%- else -%} + --menu-indent-mobile: 0 0 0 8px; + {%- endif -%} + + {%- if menu_alignment_vertical == 'top' -%} + --logo-growth-desktop: 0; + --menu-growth-desktop: 1; + --menu-border-width-desktop: 0; + --menu-bottom-border-top-width-desktop: 0; + --menu-bottom-padding-top-desktop: 0; + --menu-bottom-width-desktop: auto; + --menu-margin-x-desktop: auto; + --menu-indent-desktop: 0 20px; + --order-app-desktop: 3; + --order-button-desktop: 6; + --order-cart-desktop: 5; + --order-logo-desktop: 1; + --order-menu-desktop: 2; + --order-search-desktop: 4; + {%- else -%} + --menu-bottom-border-top-width-desktop: 100vw; + {%- if header_outline == "bordered" -%} + --menu-bottom-margin-desktop: {% if padding_bottom_desktop == 'lg' %}20px{% elsif padding_bottom_desktop == 'md' %}16px{% else %}12px{% endif %}; + --menu-bottom-border-top-width-desktop: calc(100% + var(--menu-bottom-margin-desktop) * 2); + {%- endif -%} + --menu-bottom-padding-top-desktop: {% if padding_bottom_desktop == 'lg' %}20px{% elsif padding_bottom_desktop == 'md' %}16px{% else %}12px{% endif %} 0 0; + --menu-margin-x-desktop: -22px; + --menu-indent-desktop: {% if padding_bottom_desktop == 'lg' %}20px{% elsif padding_bottom_desktop == 'md' %}16px{% else %}12px{% endif %} 0 0; + {%- endif -%} + + {%- if menu_alignment_horizontal == 'left' -%} + --menu-horizontal-position-desktop: flex-start; + {%- elsif menu_alignment_horizontal == 'right' -%} + --menu-horizontal-position-desktop: flex-end; + {%- endif -%} +{%- endcapture -%} +{% comment %} CSS variables end {% endcomment %} + +{%- for block in section.blocks -%} + {%- case block.type -%} + {%- when 'logo' -%} + {%- capture element_logo -%} + {%- assign logo = block.settings.logo_image -%} + {%- assign logo_floated = block.settings.logo_image_floated -%} + {%- assign logo_title = block.settings.logo_custom_title -%} + {%- assign svg = block.settings.logo_svg -%} + {%- assign svg_floated = block.settings.logo_svg_floated -%} + + {%- render 'logo' + logo: logo, + logo_floated: logo_floated, + logo_title: logo_title, + loading: 'eager', + routes: routes, + section_type: section_type, + shop: shop, + svg: svg, + svg_floated: svg_floated, + transparency: show_transparency + -%} + {%- endcapture -%} + + {%- when 'menu' -%} + {%- assign menu = block.settings.menu -%} + {%- assign menu_back_label = block.settings.menu_back_label -%} + + {%- if menu != blank -%} + {%- if app -%} + {%- capture element_app_mobile -%} +
+ {%- render 'app', block: app, section: section, size: "mobile" -%} +
+ {%- endcapture -%} + {%- endif -%} + + {%- capture element_menu_content -%} + {%- if show_button_mobile -%} + {{- element_button -}} + {%- endif -%} + {{- element_app_mobile -}} + {%- endcapture -%} + + {%- capture element_menu -%} + {%- render 'menu' + content: element_menu_content, + menu: menu, + menu_back_label: menu_back_label, + menu_id: block_id, + menu_levels: 3, + section_type: section_type, + settings: settings + -%} + {%- endcapture -%} + {%- endif -%} + + {%- when 'search' -%} + {%- assign show_search = block.settings.show_search -%} + + {%- if show_search -%} + {%- capture element_search -%} + {%- assign search_closer_label = block.settings.search_closer_label -%} + {%- assign search_placeholder = block.settings.search_placeholder -%} + + {%- render 'search', + closer_label: search_closer_label, + placeholder: search_placeholder, + routes: routes, + section_type: section_type + -%} + {%- endcapture -%} + {%- endif -%} + + {%- endcase -%} +{%- endfor -%} + + + +{% schema %} + { + "name": "Header", + "tag": "section", + "class": "header__wrapper", + "templates": [], + "layout": true, + "settings": [ + { + "type": "header", + "content": "General settings" + }, + { + "type": "checkbox", + "id": "sticky_header", + "default": true, + "label": "Stick to top" + }, + { + "type": "checkbox", + "id": "show_transparency", + "default": true, + "label": "Use transparent background" + }, + { + "type": "color_scheme", + "id": "color_scheme", + "label": "Color scheme", + "default": "set-1" + }, + { + "type": "header", + "content": "Desktop settings" + }, + { + "type": "select", + "id": "padding_top_desktop", + "label": "Padding top", + "options": [ + { + "value": "sm", + "label": "Small" + }, + { + "value": "md", + "label": "Medium" + }, + { + "value": "lg", + "label": "Large" + } + ], + "default": "large" + }, + { + "type": "select", + "id": "padding_bottom_desktop", + "label": "Padding bottom", + "options": [ + { + "value": "sm", + "label": "Small" + }, + { + "value": "md", + "label": "Medium" + }, + { + "value": "lg", + "label": "Large" + } + ], + "default": "large" + }, + { + "type": "select", + "id": "header_outline", + "label": "Header outline", + "options": [ + { + "value": "none", + "label": "None" + }, + { + "value": "bordered", + "label": "Bordered" + } + ], + "default": "none" + }, + { + "type": "header", + "content": "Mobile settings" + }, + { + "type": "select", + "id": "padding_top_mobile", + "label": "Padding top", + "options": [ + { + "value": "sm", + "label": "Small" + }, + { + "value": "md", + "label": "Medium" + }, + { + "value": "lg", + "label": "Large" + } + ], + "default": "medium" + }, + { + "type": "select", + "id": "padding_bottom_mobile", + "label": "Padding bottom", + "options": [ + { + "value": "sm", + "label": "Small" + }, + { + "value": "md", + "label": "Medium" + }, + { + "value": "lg", + "label": "Large" + } + ], + "default": "medium" + } + ], + "blocks": [ + { + "type": "logo", + "name": "Logo", + "limit": 1, + "settings": [ + { + "type": "header", + "content": "General settings" + }, + { + "type": "textarea", + "id": "logo_svg", + "label": "SVG logo" + }, + { + "type": "image_picker", + "id": "logo_image", + "label": "Image logo", + "info": "Displayed if no SVG code is added" + }, + { + "type": "header", + "content": "Optional settings" + }, + { + "type": "paragraph", + "content": "Displayed if header background is transparent" + }, + { + "type": "textarea", + "id": "logo_svg_floated", + "label": "SVG logo" + }, + { + "type": "image_picker", + "id": "logo_image_floated", + "label": "Image logo", + "info": "Displayed if no SVG code is added" + }, + { + "type": "header", + "content": "Alternative settings" + }, + { + "type": "paragraph", + "content": "Displayed when no logo is added" + }, + { + "type": "text", + "id": "logo_custom_title", + "label": "Site title" + } + ] + }, + { + "type": "menu", + "name": "Menu", + "limit": 1, + "settings": [ + { + "type": "header", + "content": "General settings" + }, + { + "type": "menu", + "id": "menu", + "label": "Menu", + "default": "main-menu" + }, + { + "type": "header", + "content": "Desktop settings" + }, + { + "type": "select", + "id": "menu_alignment_horizontal", + "label": "Horizontal position", + "options": [ + { + "value": "left", + "label": "Left" + }, + { + "value": "center", + "label": "Center" + }, + { + "value": "right", + "label": "Right" + } + ], + "default": "center" + }, + { + "type": "select", + "id": "menu_alignment_vertical", + "label": "Vertical alignment", + "options": [ + { + "value": "top", + "label": "Top" + }, + { + "value": "bottom", + "label": "Bottom" + } + ], + "default": "top" + }, + { + "type": "header", + "content": "Mobile settings" + }, + { + "type": "select", + "id": "menu_opener_position", + "label": "Opener position", + "options": [ + { + "value": "left", + "label": "Left" + }, + { + "value": "right", + "label": "Right" + } + ], + "default": "left" + }, + { + "type": "text", + "id": "menu_back_label", + "label": "'Back' button label" + } + ] + }, + { + "type": "search", + "name": "Search", + "limit": 1, + "settings": [ + { + "type": "header", + "content": "General settings" + }, + { + "type": "checkbox", + "id": "show_search", + "default": true, + "label": "Show search" + }, + { + "type": "text", + "id": "search_closer_label", + "label": "'Close' button label", + "default": "Close" + }, + { + "type": "text", + "id": "search_placeholder", + "label": "Placeholder", + "default": "Search" + } + ] + }, + { + "type": "button", + "name": "Button", + "limit": 1, + "settings": [ + { + "type": "header", + "content": "General settings" + }, + { + "type": "text", + "id": "button_label", + "label": "Label", + "default": "Contact us" + }, + { + "type": "url", + "id": "button_url", + "label": "URL" + }, + { + "type": "header", + "content": "Desktop settings" + }, + { + "type": "checkbox", + "id": "show_button_desktop", + "label": "Show/hide button", + "default": true + }, + { + "type": "header", + "content": "Mobile settings" + }, + { + "type": "checkbox", + "id": "show_button_mobile", + "label": "Show/hide button", + "default": true + } + ] + }, + { + "type": "@app" + } + ] + } +{% endschema %} diff --git a/snippets/button.liquid b/snippets/button.liquid new file mode 100644 index 0000000..7ea0b2b --- /dev/null +++ b/snippets/button.liquid @@ -0,0 +1,29 @@ +{%- comment -%} + This snippet renders a button with customizable label, URL, and style. + + Parameters: + - button_label: the text to display on the button + - button_url: the URL the button links to + - button_type: the style of the button (e.g., 'primary', 'outline') + - section_type: the section type where the button is included (e.g. 'header', 'footer') + + Required parameters: + - button_label + - button_url + - section_type + + Usage: + + {%- render 'button', + button_label: 'Click me', + button_url: '/your-link', + button_type: 'primary', + section_type: section.type + -%} +{%- endcomment -%} + + + + {{- button_label -}} + + diff --git a/snippets/css-mini-cart-button.liquid b/snippets/css-mini-cart-button.liquid new file mode 100644 index 0000000..8ec3c22 --- /dev/null +++ b/snippets/css-mini-cart-button.liquid @@ -0,0 +1,34 @@ +{%- style -%} + bq-minicart-button { + display: flex; + align-items: center; + justify-content: center; + height: var(--button-height); + width: var(--button-width); + background: color-mix(in srgb, var(--color-primary) 15%, transparent); + border-radius: var(--button-border-radius); + + --minicart-button-accent-color: var(--background-accent); + --minicart-button-badge-color: var(--color-accent); + --minicart-button-badge-size: 12px; + --minicart-button-border-radius: 50%; + --minicart-button-color: var(--color-primary); + --minicart-button-font: var(--font-body, --minicart-font, sans-serif); + --minicart-button-font-size: calc(var(--font-size-base) - 2px); + --minicart-button-hover-color: var(--color-outline); + --minicart-button-hover-opacity: 1; + --minicart-button-padding: 4px; + --minicart-button-size: 21px; + --minicart-button-transition-delay: 0ms; + --minicart-button-transition-duration: var(--transition-duration); + --minicart-button-transition-property: all; + --minicart-button-transition-timing-function: var(--transition-function); + --minicart-button-outer-background: transparent; + --minicart-button-outer-background-hover: transparent; + --minicart-button-outer-border: transparent; + --minicart-button-outer-border-radius: 0; + --minicart-button-outer-hover-border: transparent; + --minicart-button-outer-padding: 7px 3px 2px 2px; + --minicart-button-outer-transition: none; + } +{%- endstyle -%} diff --git a/snippets/root-variables.liquid b/snippets/css-root-variables.liquid similarity index 81% rename from snippets/root-variables.liquid rename to snippets/css-root-variables.liquid index 026d709..d2b18d4 100644 --- a/snippets/root-variables.liquid +++ b/snippets/css-root-variables.liquid @@ -10,9 +10,7 @@ --background-accent: {{ scheme.settings.background_accent }}; --background-primary: {{ scheme.settings.background }}; --color-accent: {{ scheme.settings.color_accent }}; - --color-border: {{ scheme.settings.color_primary | append: '26' }}; --color-outline: {{ scheme.settings.color_accent_outline }}; - --color-placeholder: {{ scheme.settings.color_primary | append: '80' }}; --color-primary: {{ scheme.settings.color_primary }}; --color-secondary: {{ scheme.settings.color_secondary }}; } @@ -21,19 +19,29 @@ :root { {% case settings.corner_radius %} {%- when 'square' -%} - --border-radius: 0; + --border-radius: 0; {%- when 'rounded' -%} - --border-radius: 8px; + --border-radius: 8px; {%- when 'fully_round' -%} - --border-radius: 50px; + --border-radius: 50px; {% endcase %} {% case settings.button_corners %} {%- when 'square' -%} - --border-radius-button: 0; + --button-border-radius: 0; {%- when 'rounded' -%} - --border-radius-button: 8px; + --button-border-radius: 8px; {%- when 'fully_round' -%} - --border-radius-button: 50px; + --button-border-radius: 50px; + {% endcase %} + --button-height: 40px; + --button-width: 40px; + {% case settings.button_size %} + {%- when 'sm' -%} + --button-padding: 5px 12px; + {%- when 'md' -%} + --button-padding: 5px 16px; + {%- when 'xl' -%} + --button-padding: 5px 20px; {% endcase %} --font-body: "{{ settings.font_body.family | default: Roboto }}", {{ settings.font_body.fallback_families | default: sans-serif }}; --font-heading: "{{ settings.font_heading.family | default: Nippo }}", {{ settings.font_heading.fallback_families | default: sans-serif }}; @@ -63,18 +71,29 @@ --font-weight-semibold: {{ settings.font_weight_semibold | default: "600" }}; --font-weight-bold: {{ settings.font_weight_bold | default: "700" }}; --font-weight-extrabold: {{ settings.font_weight_extrabold | default: "800" }}; - {% case settings.button_size %} + {%- case settings.social_icon_size -%} {%- when 'sm' -%} - --padding-button: 8px 12px; + --social-size: 18px; {%- when 'md' -%} - --padding-button: 10px 16px; - {%- when 'xl' -%} - --padding-button: 12px 20px; - {% endcase %} + --social-size: 21px; + {%- when 'lg' -%} + --social-size: 24px; + {%- endcase -%} + + {%- case settings.social_icon_spacing -%} + {%- when 'sm' -%} + --social-icon-spacing: 5px; + {%- when 'md' -%} + --social-icon-spacing: 7px; + {%- when 'lg' -%} + --social-icon-spacing: 9px; + {%- endcase -%} + {% if settings.transition_duration == "0" or settings.transition_duration == blank %} --transition-duration: 0.3s; {% else %} --transition-duration: {{ settings.transition_duration | append: "s" }}; {% endif %} + --transition-function: ease-in-out; } diff --git a/snippets/icon-arrow.liquid b/snippets/icon-arrow.liquid new file mode 100644 index 0000000..43c2ef9 --- /dev/null +++ b/snippets/icon-arrow.liquid @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/snippets/icon-search.liquid b/snippets/icon-search.liquid new file mode 100644 index 0000000..4879a24 --- /dev/null +++ b/snippets/icon-search.liquid @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/snippets/image.liquid b/snippets/image.liquid new file mode 100644 index 0000000..37db151 --- /dev/null +++ b/snippets/image.liquid @@ -0,0 +1,383 @@ +{%- comment -%} + + This snippet is used for rendering the images with optimal performance + + Parameters: + - image: the image object to render + - image_alt: alt text for the image + - image_class: a CSS class for the image + - image_mobile: the image that should be rendered on mobile devices + - image_size: predefined image size based on its expected width on the screen (default is 'm') + - custom_height: custom image height if predefined height doesn't fit + - custom_size: custom image size if predefined sizes don't fit + - custom_width: custom image width if predefined width doesn't fit + - fetchpriority: fetch priority for the image (default is 'auto') + - focal: focal point for the product images + - keep_type: whether to keep the original png or jpeg image type (intended for the store logo) + - loading: loading strategy for the image (default is 'lazy') + - settings: theme settings + - use_ratio: whether to use aspect ratio for the collection and product images (default is true) + + Required parameters: + - image + - image_class + - image_size OR custom_size and custom_width OR custom_size and custom_height + - settings + + Usage: + + {%- render 'image', + image: image, + image_mobile: image_mobile, + image_alt: 'image_alt', + image_class: 'your_class', + image_size: 'm', <-------- use only this ONE property + custom_size: '170px', <---- or these TWO properties ('custom_size' and 'custom_width' OR 'custom_size' and 'custom_height') + custom_width: '170', <---- instead of 'image_size' property + custom_height: '170', + focal: points, + loading: 'eager', + use_ratio: - true, + fetchpriority: 'high', - use for hero images or important visible content + keep_type: true, - image type will be kept (only png and jpeg) + settings: settings + -%} + + Explanation how to use 'image_size' property. + Image_size ensures we load the correct image size based on its width on the screen. + Media queries are built relative to the viewport width, so we define image size depending on expected width relative to the viewport. + + For example: + A hero image would be approximately as wide as the whole viewport, so we would use the 'xxl' size. + A testimonial image would be approximately as wide as half of the viewport (as it is show on the side), so we would use the 'l' size. + + xs - Image is wide as 1/16 of the viewport + s - Image is wide as 1/8 of the viewport + m - Image is wide as 1/4 of the viewport + l - Image is wide as half of the viewport + xl - Image is wide as 3/4 of the viewport + xxl - Image is as wide as the whole viewport + flexible - Image is wide as the viewport on small screens but is smaller relative to the screen on larger screens. + logo - Image has the same size and quality on all resolutions + +{%- endcomment -%} + +{%- assign image_url = image.url -%} +{%- assign image_url_mobile = image_mobile.url -%} + +{%- if image_url != blank or image_url_mobile != blank -%} + {%- assign breakpoint_md = '1024' | plus: 0 -%} + {%- assign breakpoint_sm = '768' | plus: 0 -%} + {%- assign dynamic_url = image_url -%} + {%- assign featured_type = '' -%} + {%- assign featured_types = 'webp, jpg, jpeg, png' | split: ', ' -%} + {%- assign fetch_priority = fetchpriority | default: 'auto' -%} + {%- assign has_dimensions = false -%} + {%- assign inner_height = image.height -%} + {%- assign inner_width = image.width -%} + {%- assign image_alt = image_alt | default: image.alt | default: '' -%} + {%- assign image_fit = settings.card_image_fit -%} + {%- assign image_main_class = image_class | append: ' image-main' -%} + {%- assign image_use_focal = settings.use_focal_images -%} + {%- assign loading_strategy = loading | default: 'lazy' -%} + {%- assign placeholder_height = '100%' -%} + + {%- if use_ratio == false -%} + {%- assign set_ratio = false -%} + {%- else -%} + {%- assign set_ratio = true -%} + {%- endif -%} + + {%- if loading_strategy == 'lazy' -%} + {%- assign placeholder_width = '25' | plus: 0 -%} + {%- assign reduction_factor = inner_width | divided_by: placeholder_width | plus: 0.0 -%} + {%- endif -%} + + {%- if image_url_mobile != blank -%} + {%- assign dynamic_url = image_url_mobile -%} + {%- endif -%} + + {%- if inner_width and inner_height -%} + {%- assign has_dimensions = true -%} + {%- endif -%} + + {% comment %} use native image dimensions to generate optimal sizes {% endcomment %} + {%- if has_dimensions -%} + {%- assign breakpoints = '' -%} + {%- assign paths = '' -%} + {%- assign sizes = '320, 425, 535, 750, 810, 1024, 1200, 1440, 1920, 2500' | split: ', ' -%} + {%- assign sizes_map = '' -%} + {%- assign source_map = '' -%} + {%- assign threshold = '' -%} + + {%- if loading_strategy == 'lazy' -%} + {%- assign placeholder_height = inner_height | divided_by: reduction_factor | round: 0 -%} + {%- endif -%} + + {% comment %} filter out sizes larger than the original image to avoid upscaling {% endcomment %} + {%- for item in sizes -%} + {%- assign size = item | plus: 0 -%} + + {%- if inner_width >= size -%} + {%- assign threshold = size -%} + {%- assign sizes_map = sizes_map | append: size | append: ', ' -%} + {%- continue -%} + {%- endif -%} + {%- endfor -%} + + {% comment %} clean up trailing signs {% endcomment %} + {%- assign sizes = '' -%} + {%- for item in sizes_map -%} + {%- assign size = item -%} + {%- if forloop.last -%} + {%- assign size = item | replace_last: ', ', '' -%} + {%- endif -%} + {%- assign sizes = sizes | append: size -%} + {%- endfor -%} + + {% comment %} generate srcset paths and responsive breakpoints {% endcomment %} + {%- assign sizes_map = sizes | split: ', ' -%} + {%- for item in sizes_map -%} + {%- unless forloop.last -%} + {%- assign comma = ', ' -%} + {%- else -%} + {%- assign comma = '' -%} + {%- endunless -%} + + {%- assign breakpoint = '' -%} + {%- assign size = item | plus: 0 -%} + {%- assign url = dynamic_url | image_url: width: size -%} + {%- assign path = url | append: ' ' | append: size | append: 'w' -%} + {%- assign paths = paths | append: path | append: comma -%} + {%- assign point = item | append: 'px' -%} + + {% comment %} add media query for sizes up to medium breakpoint {% endcomment %} + {%- if size <= breakpoint_md -%} + {%- assign breakpoint = '(max-width: ' | append: point | append: ') ' -%} + {%- endif -%} + + {%- assign breakpoints = breakpoints | append: breakpoint | append: point | append: comma -%} + {%- endfor -%} + + {% comment %} add original image size if not already included in filtered sizes {% endcomment %} + {%- if threshold != inner_width -%} + {%- assign img_points = '' -%} + {%- assign img_size = inner_width | append: 'px' -%} + {%- assign img_path = image_url | image_url: width: inner_width | append: ' ' | append: inner_width | append: 'w' -%} + {%- assign sizes = sizes | append: ', ' | append: inner_width -%} + {%- assign paths = paths | append: ', ' | append: img_path -%} + {%- if threshold <= breakpoint_md -%} + {%- assign img_points = '(max-width: ' | append: img_size | append: ') ' -%} + {%- endif -%} + {%- assign breakpoints = breakpoints | append: ', ' | append: img_points | append: img_size -%} + {%- endif -%} + + {%- assign source_map = sizes | split: ', '-%} + {% comment %} calculate aspect ratio for native CSS aspect-ratio property {% endcomment %} + {%- assign aspect_ratio = inner_width | times: 1.0 | divided_by: inner_height | round: 4 -%} + {%- else -%} + {%- assign image_size = image_size | default: 'm' -%} + + {%- case image_size -%} + {%- when 'xs' -%} + {%- assign sizes = '20, 30, 48, 75, 105, 160' -%} + {%- assign points = '(max-width: 320px) 20px, (max-width: 480px) 30px, (max-width: 768px) 48px, (max-width: 1200px) 75px, (max-width: 1680px) 105px, 160px' -%} + {%- assign width = '160' -%} + {%- when 's' -%} + {%- assign sizes = '40, 60, 96, 150, 210, 320' -%} + {%- assign points = '(max-width: 320px) 40px, (max-width: 480px) 60px, (max-width: 768px) 96px, (max-width: 1200px) 150px, (max-width: 1680px) 210px, 320px' -%} + {%- assign width = '320' -%} + {%- when 'm' -%} + {%- assign sizes = '80, 120, 192, 300, 420, 640' -%} + {%- assign points = '(max-width: 320px) 80px, (max-width: 480px) 120px, (max-width: 768px) 192px, (max-width: 1200px) 300px, (max-width: 1680px) 420px, 640px' -%} + {%- assign width = '640' -%} + {%- when 'l' -%} + {%- assign sizes = '160, 240, 384, 600, 840, 1280' -%} + {%- assign points = '(max-width: 320px) 160px, (max-width: 480px) 240px, (max-width: 768px) 384px, (max-width: 1200px) 600px, (max-width: 1680px) 840px, 1280px' -%} + {%- assign width = '1280' -%} + {%- when 'xl' -%} + {%- assign sizes = '240, 360, 576, 900, 1260, 1920' -%} + {%- assign points = '(max-width: 320px) 240px, (max-width: 480px) 360px, (max-width: 768px) 576px, (max-width: 1200px) 900px, (max-width: 1680px) 1260px, 1920px' -%} + {%- assign width = '1920' -%} + {%- when 'xxl' -%} + {%- assign sizes = '320, 480, 768, 1200, 1680, 2560' -%} + {%- assign points = '(max-width: 320px) 320px, (max-width: 480px) 480px, (max-width: 768px) 768px, (max-width: 1200px) 1200px, (max-width: 1680px) 1680px, 2560px' -%} + {%- assign width = '2560' -%} + {%- when 'flexible' -%} + {%- assign sizes = '320, 480, 576, 900, 840, 1280' -%} + {%- assign points = '(max-width: 320px) 320px, (max-width: 480px) 480px, (max-width: 768px) 576px, (max-width: 1200px) 900px, (max-width: 1680px) 840px, 1280px' -%} + {%- assign width = '1280' -%} + {%- when 'logo' -%} + {%- assign sizes = '420, 640' -%} + {%- assign points = '(max-width: 360px) 420px, 640px' -%} + {%- assign width = '640' -%} + {%- endcase -%} + + {% comment %} override sizes and points with custom values that usually have only one width/size {% endcomment %} + {%- if custom_width != blank and custom_size != blank -%} + {%- assign sizes = custom_width -%} + {%- assign points = custom_size -%} + {%- assign width = custom_width | split: ',' | last | plus: 0 -%} + {%- endif -%} + + {% comment %} override height with custom value {% endcomment %} + {%- if custom_height != blank -%} + {%- assign height = custom_height | split: ',' | last | plus: 0 -%} + {%- endif -%} + + {%- assign inner_height = height | default: '100%' -%} + {%- assign inner_width = width -%} + + {% comment %} set image src {% endcomment %} + {%- assign paths = '' -%} + {%- assign sizes = sizes | split: ', ' -%} + + {%- for size in sizes -%} + {%- assign img_path = dynamic_url | image_url: width: size | append: ' ' | append: size | append: 'w' -%} + + {%- if forloop.first == false -%} + {%- assign paths = paths | append: ', ' -%} + {%- endif -%} + + {%- assign paths = paths | append: img_path -%} + {%- endfor -%} + + {%- assign source_map = sizes -%} + {%- assign breakpoints = points -%} + {%- endif -%} + + {% comment %} get initial image type and ensure we support it {% endcomment %} + {%- for type in featured_types -%} + {%- assign image_type = '.' | append: type -%} + + {%- if featured_type == '' and image_url contains image_type -%} + {%- assign featured_type = image_type -%} + {%- break -%} + {%- endif -%} + {%- endfor -%} + + {% comment %} convert images to the most in-demand formats {% endcomment %} + {%- if featured_type != blank and image_url contains featured_type -%} + {%- assign src_jpg = '' -%} + {%- assign src_png = '' -%} + {%- assign src_webp = '' -%} + + {%- if featured_type != '.jpg' and featured_type != '.jpeg' -%} + {%- assign jpg = 'jpg' -%} + {%- endif -%} + {%- if featured_type != '.png' -%} + {%- assign png = 'png' -%} + {%- endif -%} + {%- if featured_type != '.webp' -%} + {%- assign webp = 'webp' -%} + {%- endif -%} + + {%- for item in source_map -%} + {%- assign path_jpg = image_url | image_url: width: item, format: jpg | append: ' ' | append: item | append: 'w' -%} + {%- assign path_png = image_url | image_url: width: item, format: png | append: ' ' | append: item | append: 'w' -%} + {%- assign path_webp = image_url | image_url: width: item, format: webp | append: ' ' | append: item | append: 'w' -%} + + {%- if forloop.first == false -%} + {%- assign src_jpg = src_jpg | append: ', ' -%} + {%- assign src_png = src_png | append: ', ' -%} + {%- assign src_webp = src_webp | append: ', ' -%} + {%- endif -%} + + {%- assign src_jpg = src_jpg | append: path_jpg -%} + {%- assign src_png = src_png | append: path_png -%} + {%- assign src_webp = src_webp | append: path_webp -%} + {%- endfor -%} + {%- endif -%} + + {%- if keep_type and featured_type == '.png' or keep_type and featured_type == '.jpg' or keep_type and featured_type == '.jpeg' -%} + {%- assign keep_type = true -%} + {%- endif -%} + + {%- if image_use_focal and image_fit == 'cover' -%} + {%- assign image_focal_points = focal | remove: '[' | remove: ']' | strip | split: ',' -%} + {%- assign image_focal_x = image_focal_points[0] -%} + {%- assign image_focal_y = image_focal_points[1] -%} + + {%- if image_focal_x contains '.' or image_focal_y contains '.' -%} + {%- assign image_points = true -%} + {%- endif -%} + {%- endif -%} + + + {% comment %} load low quality image placeholder {% endcomment %} + {%- if loading_strategy == 'lazy' -%} + {%- unless image_points -%} + + {%- endunless -%} + {%- endif -%} + + {% comment %} load high quality multiformat image {% endcomment %} + + {% unless keep_type %} + {%- if featured_type == '.webp' or featured_type == '.jpg' or featured_type == '.jpeg' or featured_type == '.png' -%} + + {%- endif -%} + {% endunless %} + + {%- if featured_type == '.jpg' or featured_type == '.jpeg' -%} + + {%- endif -%} + + {%- if featured_type == '.png' -%} + + {%- endif -%} + + {{ image_alt | escape }} + + +{%- endif -%} diff --git a/snippets/logo.liquid b/snippets/logo.liquid new file mode 100644 index 0000000..365f57a --- /dev/null +++ b/snippets/logo.liquid @@ -0,0 +1,107 @@ +{%- comment -%} + This snippet renders the logo image, SVG or text title + + Parameters: + - logo: the logo image object + - logo_floated: the floating logo image object + - logo_title: the custom text title for the logo + - loading: 'eager' or 'lazy' (default is 'lazy') + - routes: the routes object + - section_type: the section type where the logo is included (e.g. 'header', 'footer') + - shop: the shop object + - svg: the logo SVG code + - svg_floated: the floating logo SVG code + - transparency: whether to show floating logo or SVG + + Required parameters: + - logo OR svg + - routes + - section_type + - shop + + Usage: + + {%- render 'logo', + logo: logo_image, + logo_floated: logo_image_floated, + logo_title: 'Your Title', + loading: 'eager', + routes: routes, + section_type: section.type, + shop: shop, + svg: svg_code, + svg_floated: svg_code_floating, + transparency: true + -%} +{%- endcomment -%} + +{%- assign loading_strategy = loading | default: 'lazy' -%} + + + diff --git a/snippets/menu.liquid b/snippets/menu.liquid new file mode 100644 index 0000000..1ecf76e --- /dev/null +++ b/snippets/menu.liquid @@ -0,0 +1,162 @@ +{%- comment -%} + + This snippet renders a menu with support for dropdowns + + Parameters: + - content: additional content to include below the menu items (default is empty) + - menu: the navigation menu object to render + - menu_back_label: label for the 'Back' button in mobile dropdowns (default is 'link.title') + - menu_id: unique ID for the menu instance (e.g. block.id) + - menu_levels: number of menu nested dropdown menus to display (max. 3 levels) + - section_type: the section type where the menu is included (e.g. 'header', 'footer') + - settings: theme settings + - socials: social links to include at the bottom of the mobile menu (default is true) + - use_opener: whether to include a menu opener on mobile, e.g. hamburger button to open menu (default is true) + + Required parameters: + - menu + - menu_id + - section_type + - settings + + Usage: + + {%- render 'menu', + content: element_menu_content, + menu: main_menu, + menu_back_label: 'Go back', + menu_id: block.id, + menu_levels: number, + section_type: section.type, + settings: settings, + socials: false, + use_opener: false + -%} +{%- endcomment -%} + +{%- assign menu = menu | default: '' -%} + +{%- if menu != blank -%} + {%- assign content = content | default: '' -%} + {%- assign menu_levels = menu_levels | default: 1 -%} + {%- assign menu_items = menu.items -%} + {%- assign socials = socials | default: true -%} + {%- assign use_opener = use_opener | default: true -%} + + {%- capture icon_arrow -%} + + {%- render 'icon-arrow' -%} + + {%- endcapture -%} + + +{%- endif -%} + diff --git a/snippets/search.liquid b/snippets/search.liquid new file mode 100644 index 0000000..351d6af --- /dev/null +++ b/snippets/search.liquid @@ -0,0 +1,48 @@ +{%- comment -%} + This snippet renders a search form with a toggleable input field. + + Parameters: + - closer_label: label for the 'Close search' button in the search form + - routes: the routes object + - search_placeholder: placeholder text for the search input (default is 'Search...') + - section_type: the section type where the search is included (e.g. 'header', 'footer') + + Required parameters: + - routes + - section_type + + Usage: + + {%- render 'search', + closer_label: closer_label, + placeholder: search_placeholder, + routes: routes, + section_type: section.type + -%} +{%- endcomment -%} + + diff --git a/snippets/socials.liquid b/snippets/socials.liquid new file mode 100644 index 0000000..6d5a99b --- /dev/null +++ b/snippets/socials.liquid @@ -0,0 +1,59 @@ +{%- comment -%} + + This snippet is used for rendering the social networks icons + + Usage: + + {%- render 'socials', settings: settings -%} + +{%- endcomment -%} + +{%- assign names = 'instagram, tiktok, facebook, x, linkedin, youtube, vimeo, pinterest, snapchat, tumblr' | split: ', ' -%} + +{%- capture links_arr -%} + {{- settings.social_instagram -}}, + {{- settings.social_tiktok -}}, + {{- settings.social_facebook -}}, + {{- settings.social_x -}}, + {{- settings.social_linkedin -}}, + {{- settings.social_youtube -}}, + {{- settings.social_vimeo -}}, + {{- settings.social_pinterest -}}, + {{- settings.social_snapchat -}}, + {{- settings.social_tumblr }} +{%- endcapture -%} + +{%- assign links = links_arr | split: ',' -%} + +{%- if links.size > 0 -%} + +{%- endif -%} + diff --git a/src/config/settings_data.json b/src/config/settings_data.json index 3952127..3dcd1af 100644 --- a/src/config/settings_data.json +++ b/src/config/settings_data.json @@ -1,7 +1,7 @@ { "current": { "button_corners": "square", - "button_font_size": 18, + "button_font_size": 16, "button_font_weight": "700", "button_size": "md", "color_schemes": { @@ -63,12 +63,68 @@ "font_weight_h2": "700", "font_weight_h3": "700", "font_weight_headers": "700", + "icon_style": "outline", "sections": { "header": { "type": "header", - "blocks": {}, - "block_order": [], + "blocks": { + "logo": { + "type": "logo", + "settings": { + "logo_custom_title": "{{ shop.name }}", + "logo_image": "", + "logo_image_floated": "", + "logo_svg": "", + "logo_svg_floated": "" + }, + "disabled": null + }, + "menu": { + "type": "menu", + "settings": { + "menu": "main-menu", + "menu_back_label": "Back", + "menu_alignment_horizontal": "center", + "menu_alignment_vertical": "top", + "menu_opener_position": "left" + }, + "disabled": null + }, + "search": { + "type": "search", + "settings": { + "search_closer_label": "Close", + "search_placeholder": "Search", + "show_search": true + }, + "disabled": null + }, + "button": { + "type": "button", + "settings": { + "button_label": "Contact us", + "button_url": "booqable://pages/contact", + "show_button_desktop": true, + "show_button_mobile": true + }, + "disabled": null + } + }, + "block_order": [ + "logo", + "menu", + "search", + "button" + ], "settings": { + "color_scheme": "set-1", + "header_outline": "none", + "padding_bottom_desktop": "lg", + "padding_bottom_mobile": "md", + "padding_top_desktop": "lg", + "padding_top_mobile": "md", + "show_transparency": true, + "sticky_header": true }, "disabled": null } diff --git a/src/config/settings_schema.json b/src/config/settings_schema.json index 500a2a1..4df2598 100644 --- a/src/config/settings_schema.json +++ b/src/config/settings_schema.json @@ -235,6 +235,127 @@ } ] }, + { + "name": "Social media", + "settings": [ + { + "type": "header", + "content": "Sizes" + }, + { + "type": "select", + "id": "social_icon_size", + "label": "Size", + "options": [ + { + "value": "sm", + "label": "Small" + }, + { + "value": "md", + "label": "Medium" + }, + { + "value": "lg", + "label": "Large" + } + ], + "default": "md" + }, + { + "type": "select", + "id": "social_icon_spacing", + "label": "Spacing", + "options": [ + { + "value": "sm", + "label": "Small" + }, + { + "value": "md", + "label": "Medium" + }, + { + "value": "lg", + "label": "Large" + } + ], + "default": "md" + }, + { + "type": "header", + "content": "Links" + }, + { + "type": "paragraph", + "content": "Note: Leave the field blank if you don't want to use the social network" + }, + { + "type": "text", + "id": "social_facebook", + "label": "Facebook", + "default": "https://facebook.com/", + "info": "Example: https://facebook.com/booqable" + }, + { + "type": "text", + "id": "social_instagram", + "label": "Instagram", + "default": "https://instagram.com/", + "info": "Example: https://instagram.com/booqable" + }, + { + "type": "text", + "id": "social_x", + "label": "X (ex. Twitter)", + "default": "https://x.com/", + "info": "Example: https://x.com/booqable" + }, + { + "type": "text", + "id": "social_linkedin", + "label": "Linkedin", + "default": "https://linkedin.com/", + "info": "Example: https://www.linkedin.com/company/booqable" + }, + { + "type": "text", + "id": "social_pinterest", + "label": "Pinterest", + "info": "Example: https://pinterest.com/booqable" + }, + { + "type": "text", + "id": "social_tiktok", + "label": "TikTok", + "info": "Example: https://tiktok.com/@booqable" + }, + { + "type": "text", + "id": "social_tumblr", + "label": "Tumblr", + "info": "Example: https://booqable.tumblr.com" + }, + { + "type": "text", + "id": "social_snapchat", + "label": "Snapchat", + "info": "Example: https://snapchat.com/add/booqable" + }, + { + "type": "text", + "id": "social_youtube", + "label": "YouTube", + "info": "Example: https://youtube.com/booqable" + }, + { + "type": "text", + "id": "social_vimeo", + "label": "Vimeo", + "info": "Example: https://vimeo.com/booqable" + } + ] + }, { "name": "Typography", "settings": [ diff --git a/src/js/example.js b/src/js/example.js deleted file mode 100644 index 366cf91..0000000 --- a/src/js/example.js +++ /dev/null @@ -1,3 +0,0 @@ -// Example JavaScript file - replace with actual functionality -// eslint-disable-next-line no-console -console.log('Example JavaScript file loaded successfully!') diff --git a/src/js/main.js b/src/js/main.js new file mode 100644 index 0000000..19b786b --- /dev/null +++ b/src/js/main.js @@ -0,0 +1,42 @@ +/** + * Main Component + * + * Handles core functionality for the site: + * - Page loaded state management + * + */ + +import { + frameSequence, + onReady +} from 'utils.js' + +const MainConfig = { + selector: { + doc: document.documentElement + }, + modifier: { + loaded: 'loaded' + } +} + +const MainDOM = { + setClassLoaded() { + const read = () => ({ + doc: MainConfig.selector.doc, + modifier: MainConfig.modifier.loaded + }) + + const write = (data) => { + data.doc.classList.add(data.modifier) + } + + frameSequence(read, write) + } +} + +const initMain = () => { + MainDOM.setClassLoaded() +} + +onReady(initMain) diff --git a/src/js/utils.js b/src/js/utils.js new file mode 100644 index 0000000..3f80b94 --- /dev/null +++ b/src/js/utils.js @@ -0,0 +1,220 @@ +/** + * JavaScript Utilities + * + * Performance-optimized utilities for DOM manipulation, + * event handling, and component management. + */ + +/* eslint-env browser */ +/* global clearTimeout */ +/* global setTimeout */ +/* global requestAnimationFrame */ +/* global requestIdleCallback */ +/* global ResizeObserver */ +/* global getComputedStyle */ + +/** + * Debounce utility - limits function execution frequency + */ +export const debounce = (func, wait) => { + let timeout + return (...args) => { + clearTimeout(timeout) + timeout = setTimeout(() => func.apply(this, args), wait) + } +} + +/** + * Single frame utility - schedules callback for next animation frame + */ +export const nextFrame = (callback) => requestAnimationFrame(callback) + +/** + * Frame sequencing for optimal performance + * Separates DOM reads and writes to prevent layout thrashing + */ +export const frameSequence = (readCallback, writeCallback) => { + nextFrame(() => { + const data = readCallback() + nextFrame(() => writeCallback(data)) + }) +} + +/** + * Idle callback (with fallback for Safari) + */ +export const requestIdle = (callback, options = {}) => { + if ('requestIdleCallback' in window) { + return requestIdleCallback(callback, options) + } + + const timeout = options.timeout || 100 + const startTime = Date.now() + + return setTimeout(() => { + callback({ + didTimeout: false, + timeRemaining: () => Math.max(0, 50 - (Date.now() - startTime)) + }) + }, timeout) +} + +/** + * Create a ResizeObserver with debouncing + */ +export const resizeObserver = (callback, options = {}) => { + const defaultOptions = { + debounceTime: 150, + element: null, + width: true + } + + const config = { ...defaultOptions, ...options } + let observer = null + let lastWidth = window.innerWidth + + const frame = (data) => nextFrame(() => callback(data)) + + const debounced = config.debounceTime > 0 + ? debounce(frame, config.debounceTime) + : frame + + const trackWidthChange = () => { + if (!config.width) return false + const currentWidth = window.innerWidth + if (currentWidth === lastWidth) return true + lastWidth = currentWidth + return false + } + + const handleCallback = (data) => { + if (trackWidthChange()) return + debounced(data) + } + + const init = (element = config.element) => { + if (observer) return observer + + observer = new ResizeObserver(handleCallback) + + element ? + observer.observe(element) : + observer.observe(document.documentElement) + + return observer + } + + const destroy = () => { + observer?.disconnect() + observer = null + } + + const observe = (element) => { + if (!observer) init() + observer?.observe(element) + } + + const unobserve = (element) => { + observer?.unobserve(element) + } + + return { + init, + destroy, + observe, + unobserve, + get observer() { return observer } + } +} + +/** + * Global cleanup system to prevent memory leaks + */ +export const cleanupSystem = () => { + const cleanupFunctions = new Map() + + const register = (name, cleanupFn) => { + if (typeof cleanupFn !== 'function') { + console.warn(`Cleanup function for "${name}" is not a function`) + return + } + + // Store the cleanup function + cleanupFunctions.set(name, cleanupFn) + + // Add to global cleanup if it exists + if (window.themeCleanup) { + const originalCleanup = window.themeCleanup + window.themeCleanup = () => { + cleanupFn() + originalCleanup() + } + } else { + window[`cleanup${name}`] = cleanupFn + } + } + + const cleanup = (name) => { + const cleanupFn = cleanupFunctions.get(name) + if (cleanupFn) { + cleanupFn() + cleanupFunctions.delete(name) + } + } + + const cleanupAll = () => { + cleanupFunctions.forEach((cleanupFn, name) => { + try { + cleanupFn() + } catch (error) { + console.warn(`Error cleaning up "${name}":`, error) + } + }) + cleanupFunctions.clear() + } + + return { + register, + cleanup, + cleanupAll, + get registered() { return Array.from(cleanupFunctions.keys()) } + } +} + +/** + * DOM ready state utility + */ +export const onReady = (callback) => { + if (document.readyState === 'complete') { + requestIdle(callback) + } else { + document.addEventListener('readystatechange', (event) => { + if (event.target.readyState === 'complete') { + requestIdle(callback) + } + }, { once: true }) + } +} + +/** + * CSS variable utilities + */ +export const setCssVar = (property, value, element = document.documentElement) => { + element.style.setProperty(property, value) +} + +export const getCssVar = (property, element = document.documentElement) => { + return getComputedStyle(element).getPropertyValue(property).trim() +} + +/** + * Element dimension utility + */ +export const getDimensions = (element) => { + if (!element) return { width: 0, height: 0 } + const rect = element.getBoundingClientRect() + return { + width: rect.width, + height: rect.height + } +} diff --git a/src/layout/theme.liquid b/src/layout/theme.liquid index 7a03b6b..8929ac5 100644 --- a/src/layout/theme.liquid +++ b/src/layout/theme.liquid @@ -19,10 +19,10 @@ {%- render 'fonts', settings: settings -%} {% comment %} Load root variables {% endcomment %} - {%- render 'root-variables', settings: settings -%} + {%- render 'css-root-variables', settings: settings -%} {% comment %} Async CSS loading with resource hints {% endcomment %} - {%- assign css_files = "critical.css, styles.css" | split: ", " -%} + {%- assign css_files = "critical.css, styles.css, header.css" | split: ", " -%} {%- for css_file in css_files -%} {%- assign css_url = css_file | asset_url -%} @@ -48,7 +48,9 @@ {%- render 'page-performance-monitoring' -%} -
+
+ {% section 'header' %} +

Welcome to My Theme

@@ -65,6 +67,13 @@ {{ content_for_body }} {% comment %} Critical JavaScript files {% endcomment %} - + + diff --git a/src/scss/_base/_base.scss b/src/scss/_base/_base.scss index d3def41..bf6a69f 100644 --- a/src/scss/_base/_base.scss +++ b/src/scss/_base/_base.scss @@ -1,3 +1,60 @@ +// Import variables +@use '../variables' as *; + +// Import mixins +@use '../_helpers/mixins' as *; + +html { + &:has(#header-opener-menu:checked), + &:has(#header-opener-search:checked) { + @include media-down(md) { + overflow: clip; + } + } +} + +body { + --overlay-transition-duration: #{$transition-duration}; + --overlay-transition-delay: 50ms; + --overlay-visibility: hidden; + + &::before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100vw; + height: 100%; + z-index: -1; + pointer-events: none; + background: $background-shadow; + visibility: var(--overlay-visibility); + transition: visibility var(--overlay-transition-duration) var(--overlay-transition-delay) $transition-function; + } + + &:has(#header-opener-menu:checked), + &:has(#header-opener-search:checked) { + @include media-down(md) { + overflow: clip; + } + + &::before { + --overlay-transition-duration: 0s; + --overlay-transition-delay: 0s; + --overlay-visibility: visible; + } + } + + &:has(.menu__item.has-dropdown:hover) { + /* stylelint-disable-next-line */ + &::before { + --overlay-transition-duration: 0s; + --overlay-transition-delay: 0s; + --overlay-visibility: visible; + } + } +} + #main { > section { /* Stretch the only section to the footer */ diff --git a/src/scss/_base/_rx.scss b/src/scss/_base/_rx.scss index 2e31e5b..5c6691e 100644 --- a/src/scss/_base/_rx.scss +++ b/src/scss/_base/_rx.scss @@ -1,6 +1,9 @@ // Import variables @use '../variables' as *; +// Import mixins +@use '../_helpers/mixins' as *; + .bq-content { &.rx-content { font-family: $font-family-body; @@ -26,7 +29,7 @@ color: $color-outline; } - &:hover { + @include hover { opacity: 0.8; h1 &, diff --git a/src/scss/_base/_typography.scss b/src/scss/_base/_typography.scss index 33d83a1..77a7475 100644 --- a/src/scss/_base/_typography.scss +++ b/src/scss/_base/_typography.scss @@ -1,6 +1,9 @@ // Import variables @use '../variables' as *; +// Import mixins +@use '../_helpers/mixins' as *; + h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 { font-family: $font-family-heading; font-weight: $font-weight-headers; @@ -81,15 +84,17 @@ strong { } a { + --link-color: inherit; + display: inline-block; vertical-align: top; background: none; - color: inherit; + color: var(--link-color); text-decoration: none; transition: all $transition-duration $transition-function; - &:hover { - opacity: 0.8; + @include hover { + --link-color: #{$color-outline}; } } @@ -148,6 +153,9 @@ hr { overflow: visible; } -.fa, .far { +.fa, .far, +[class^='fa-'], +[class*='fa-'] { color: currentcolor; + transition: all $transition-duration $transition-function; } diff --git a/src/scss/_components/_button.scss b/src/scss/_components/_button.scss index 2e8a458..ec8beca 100644 --- a/src/scss/_components/_button.scss +++ b/src/scss/_components/_button.scss @@ -1,37 +1,57 @@ // Button component styles @use '../variables' as *; +// Import mixins +@use '../_helpers/mixins' as *; + .btn { + --button-background: none; + --button-border-color: transparent; + --button-color: currentcolor; + --button-indent: 0; + --button-min-height: auto; + font-family: $font-family-body; font-size: $font-size-button; font-weight: $font-weight-button; - padding: $padding-button; margin: 0; - color: currentcolor; - background: none; + padding: var(--button-indent); + background: var(--button-background); + color: var(--button-color); text-decoration: none; - border: 1px solid transparent; - border-radius: $border-radius-button; + border: 1px solid var(--button-border-color); + border-radius: $button-border-radius; cursor: pointer; + min-height: var(--button-min-height); display: inline-flex; justify-content: center; align-items: center; - line-height: $font-height-sm; + line-height: $font-height-xs; transition: all $transition-duration $transition-function; - &:hover { + @include hover { opacity: 0.8; } &--primary { - border-color: $background-accent; - background-color: $background-accent; - color: $color-accent; + --button-background: #{$background-accent}; + --button-color: #{$color-accent}; + --button-border-color: #{$background-accent}; + --button-indent: #{$button-padding}; + --button-min-height: #{$button-height}; + + @include hover { + --button-color: #{$color-accent}; + } } &--outline { - border-color: currentcolor; - background-color: transparent; - color: currentcolor; + --button-background: transparent; + --button-color: currentcolor; + --button-border-color: currentcolor; + + @include hover { + --button-color: currentcolor; + } } } diff --git a/src/scss/_components/_logo.scss b/src/scss/_components/_logo.scss new file mode 100644 index 0000000..1baf485 --- /dev/null +++ b/src/scss/_components/_logo.scss @@ -0,0 +1,34 @@ +// Import variables +@use '../variables' as *; + +// Import mixins +@use '../_helpers/mixins' as *; + +.logo { + position: relative; + + &__image { + display: flex; + align-items: center; + max-height: 64px; + max-width: 200px; + width: auto; + + &-holder { + &--floated { + position: absolute; + top: 50%; + left: 0; + transform: translateY(-50%); + } + } + } + + &__text { + font-size: 24px; + line-height: $font-height-md; + font-weight: $font-weight-semibold; + margin: 0; + color: currentcolor; + } +} diff --git a/src/scss/_components/_menu.scss b/src/scss/_components/_menu.scss new file mode 100644 index 0000000..0f51ee2 --- /dev/null +++ b/src/scss/_components/_menu.scss @@ -0,0 +1,585 @@ +// Import variables +@use '../variables' as *; + +// Import mixins +@use '../_helpers/mixins' as *; + +.menu { + --menu-display: flex; + --menu-dropdown-events: none; + --menu-dropdown-height: calc(100vh - #{$header-height}); + --menu-dropdown-item-display: grid; + --menu-dropdown-left: auto; + --menu-dropdown-link-display: flex; + --menu-dropdown-link-font-size: 18px; + --menu-dropdown-link-font-weight: #{$font-weight-bold}; + --menu-dropdown-link-indent: 0; + --menu-dropdown-link-padding: 16px var(--menu-dropdown-link-indent) 16px 0; + --menu-dropdown-position: fixed; + --menu-dropdown-right: 0; + --menu-dropdown-top: 0; + --menu-dropdown-transform: translateX(100%); + --menu-dropdown-width: 100%; + --menu-dropdown-opener-display: flex; + --menu-dropdown-opener-height: #{$button-height}; + --menu-dropdown-opener-position: center; + --menu-dropdown-opener-transform: rotate(90deg); + --menu-dropdown-opener-weight: inherit; + --menu-dropdown-opener-width: #{$button-width}; + --menu-dropdown-wrapper-height: 100%; + --menu-dropdown-wrapper-margin: 0; + --menu-dropdown-wrapper-overflow: clip auto; + --menu-dropdown-wrapper-padding: 25px #{$horizontal-padding-mobile} 50px; + --menu-dropdown-wrapper-position: absolute; + --menu-dropdown-wrapper-transform: translateX(100%); + --menu-dropdown-wrapper-width: 100vw; + --menu-opener-color: currentcolor; + --menu-horizontal-position: flex-end; + --menu-item-cursor: default; + --menu-item-margin: 0; + --menu-item-icon-transform: rotate(-90deg); + --menu-item-icon-margin: 0; + --menu-link-indent: 0; + --menu-list-margin: 0 auto; + --menu-list-overflow: clip auto; + --menu-list-padding: 0; + --menu-list-width: 100%; + --menu-margin-bottom: var(--padding-bottom-mobile, 12px); + --menu-margin-top: var(--padding-top-mobile, 12px); + --menu-nav-delay: 50ms; + --menu-nav-duration: #{$transition-duration}; + --menu-nav-events: none; + --menu-nav-height: calc(100vh - #{$header-height}); + --menu-nav-overflow: clip auto; + --menu-nav-visibility: hidden; + --menu-nav-width: 100%; + --menu-opener-display: flex; + --menu-opener-icon-left: 27%; + --menu-opener-icon-right: 27%; + --menu-opener-icon-top: 50%; + --menu-opener-icon-transform: translate(0, -50%); + --menu-wrapper-background: #{$background-primary}; + --menu-wrapper-flex-direction: column; + --menu-wrapper-overflow: clip; + --menu-wrapper-padding: #{$horizontal-padding-mobile}; + --menu-wrapper-position: absolute; + --menu-wrapper-transform: translateY(-100%); + --menu-wrapper-width: 100vw; + + display: var(--menu-display); + align-items: center; + + @include media-up(md) { + --menu-display: block; + --menu-dropdown-item-display: block; + --menu-dropdown-position: absolute; + --menu-dropdown-top: calc(100% + 1px); + --menu-dropdown-width: 100vw; + --menu-dropdown-height: auto; + --menu-dropdown-left: 50%; + --menu-dropdown-link-display: inline-block; + --menu-dropdown-link-font-size: 20px; + --menu-dropdown-link-padding: 0; + --menu-dropdown-right: auto; + --menu-dropdown-transform: translateX(-50%); + --menu-dropdown-item-margin: 0; + --menu-dropdown-opener-height: auto; + --menu-dropdown-opener-width: auto; + --menu-dropdown-list-display: grid; + --menu-dropdown-list-gap: 20px; + --menu-dropdown-list-padding: 25px #{$horizontal-padding}; + --menu-dropdown-wrapper-height: auto; + --menu-dropdown-wrapper-margin: 0 auto; + --menu-dropdown-wrapper-overflow: auto; + --menu-dropdown-wrapper-padding: 0; + --menu-dropdown-wrapper-position: static; + --menu-dropdown-wrapper-transform: translateY(-100%); + --menu-dropdown-wrapper-width: 100%; + --menu-horizontal-position: var(--menu-horizontal-position-desktop, center); + --menu-item-margin: 0 22px; + --menu-list-overflow: inherit; + --menu-list-margin: calc(-1 * var(--menu-margin-top)) var(--menu-margin-x-desktop) calc(-1 * var(--menu-margin-bottom)); + --menu-list-width: calc(100% + -1 * var(--menu-margin-x-desktop, 0) * 2); + --menu-margin-bottom: var(--padding-bottom-desktop, 12px); + --menu-margin-top: var(--padding-top-desktop, 12px); + --menu-nav-delay: 0s; + --menu-nav-duration: 0s; + --menu-nav-events: all; + --menu-nav-height: 100%; + --menu-nav-overflow: inherit; + --menu-nav-visibility: visible; + --menu-nav-width: auto; + --menu-opener-display: none; + --menu-wrapper-background: none; + --menu-wrapper-flex-direction: row; + --menu-wrapper-overflow: inherit; + --menu-wrapper-padding: 0; + --menu-wrapper-position: static; + --menu-wrapper-transform: translateY(0); + --menu-wrapper-width: 100%; + } + + path { + stroke: var(--menu-opener-color); + stroke-width: 1; + transition: stroke $transition-duration $transition-function; + } + + &__opener { + height: $button-height; + width: $button-width; + border-radius: $button-border-radius; + display: var(--menu-opener-display); + align-items: center; + background: $button-background; + position: relative; + cursor: pointer; + + & span, + &::before, + &::after { + background: var(--menu-opener-color); + position: absolute; + top: var(--menu-opener-icon-top); + left: var(--menu-opener-icon-left); + right: var(--menu-opener-icon-right); + height: 2px; + transform: var(--menu-opener-icon-transform); + transition: all $transition-duration $transition-function; + pointer-events: none; + } + + span { + #header-opener-menu:checked ~ & { + opacity: 0; + } + } + + &::before { + --menu-opener-icon-top: calc(50% - 6px); + --menu-opener-icon-transform: translate(0, 0); + + content: ""; + + #header-opener-menu:checked ~ & { + --menu-opener-icon-transform: rotate(45deg); + --menu-opener-icon-top: 50%; + --menu-opener-icon-left: 23%; + --menu-opener-icon-right: 23%; + } + } + + &::after { + --menu-opener-icon-top: calc(50% + 4px); + --menu-opener-icon-transform: translate(0, 0); + + content: ""; + + #header-opener-menu:checked ~ & { + --menu-opener-icon-transform: rotate(-45deg); + --menu-opener-icon-top: 50%; + --menu-opener-icon-left: 23%; + --menu-opener-icon-right: 23%; + } + } + + @include hover { + &::before, + &::after, + span { + --menu-opener-color: #{$color-outline}; + } + } + } + + &__wrapper { + position: var(--menu-wrapper-position); + width: var(--menu-wrapper-width); + min-height: 100%; + top: 0; + left: 0; + z-index: 1; + transform: var(--menu-wrapper-transform); + transition: transform $transition-duration $transition-function; + display: flex; + flex-direction: var(--menu-wrapper-flex-direction); + justify-content: space-between; + overflow: var(--menu-wrapper-overflow); + color: $color-primary; + padding: var(--menu-wrapper-padding); + background: var(--menu-wrapper-background); + + @include media-up(md) { + height: 100%; + } + + > { + span, ul, div { + /* stylelint-disable-next-line */ + &:not(:only-child){ + padding: 0 0 50px; + + @include media-up(md) { + padding: 0; + } + } + } + } + } + + &__nav { + position: var(--menu-wrapper-position); + height: var(--menu-nav-height); + width: var(--menu-nav-width); + top: $header-height; + left: 0; + overflow: var(--menu-nav-overflow); + pointer-events: var(--menu-nav-events); + visibility: var(--menu-nav-visibility); + transition: visibility var(--menu-nav-duration) $transition-function var(--menu-nav-delay); + font-weight: $font-weight-regular; + display: block !important; + + #header-opener-menu:checked ~ & { + --menu-nav-events: all; + --menu-nav-visibility: visible; + --menu-nav-duration: 0s; + --menu-nav-delay: 0s; + + .menu__wrapper { + --menu-wrapper-transform: translateY(0); + } + } + } + + &__list { + list-style-type: none; + position: relative; + max-width: $max-width; + height: auto; + width: var(--menu-list-width); + overflow: var(--menu-list-overflow); + margin: var(--menu-list-margin); + padding: var(--menu-list-padding); + flex: 1 1 auto; + + @include media-up(md) { + display: flex; + justify-content: var(--menu-horizontal-position); + align-items: center; + } + + &:not(:only-child) { + --menu-list-padding: 0 0 50px; + } + } + + &__item { + display: grid; + grid-template-columns: 1fr min-content; + grid-template-areas: "item-1 item-2"; + align-items: center; + margin: var(--menu-item-margin); + cursor: var(--menu-item-cursor); + + @include media-up(md) { + height: 100%; + } + + &-icon { + display: flex; + align-items: center; + justify-content: center; + margin: var(--menu-item-icon-margin); + transform: var(--menu-item-icon-transform); + transition: transform $transition-duration $transition-function; + pointer-events: none; + } + + @include hover { + --menu-item-cursor: pointer; + + .menu { + &__link { + @include media-up(md) { + color: $color-outline; + } + } + + &__dropdown { + @include media-up(md) { + --menu-dropdown-events: all; + } + + &-wrapper { + @include media-up(md) { + --menu-dropdown-wrapper-transform: translateY(0); + } + } + + &-opener { + @include media-up(md) { + --menu-dropdown-opener-transform: rotate(270deg); + } + + path { + @include media-up(md) { + --menu-opener-color: #{$color-outline}; + } + } + } + } + } + } + } + + &__link { + font-size: 18px; + font-weight: $font-weight-bold; + line-height: $font-height-lg; + padding: 16px var(--menu-link-indent) 16px 0; + grid-area: item-1; + z-index: 1; + position: relative; + + @include media-up(md) { + padding: 0 var(--menu-link-indent) 0 0; + } + + .has-dropdown & { + --menu-link-indent: 16px; + + @include media-up(md) { + --menu-link-indent: 10px; + } + } + } + + &__dropdown { + position: var(--menu-dropdown-position); + top: var(--menu-dropdown-top); + left: var(--menu-dropdown-left); + right: var(--menu-dropdown-right); + width: var(--menu-dropdown-width); + height: var(--menu-dropdown-height); + margin: 0; + transform: var(--menu-dropdown-transform); + transition: transform $transition-duration $transition-function; + overflow: clip; + pointer-events: var(--menu-dropdown-events); + display: block !important; + z-index: 2; + + &-list { + @include media-up(md) { + padding: var(--menu-dropdown-list-padding); + display: var(--menu-dropdown-list-display); + grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); + gap: var(--menu-dropdown-list-gap); + } + + #{&} { + @include media-up(md) { + --menu-dropdown-list-display: block; + --menu-dropdown-list-padding: 0; + --menu-dropdown-list-gap: 0; + } + } + } + + &-item { + display: var(--menu-dropdown-item-display); + grid-template-columns: 1fr min-content; + grid-template-areas: "drop-1 item-2"; + align-items: center; + margin: var(--menu-dropdown-item-margin); + padding: 0; + + #{&} { + @include media-up(md) { + --menu-dropdown-item-margin: 8px 0; + } + + .menu__dropdown-link { + @include media-up(md) { + --menu-dropdown-link-font-size: 16px; + --menu-dropdown-link-font-weight: #{$font-weight-medium}; + } + } + } + } + + &-link { + display: var(--menu-dropdown-link-display); + align-items: center; + justify-content: space-between; + grid-area: drop-1; + padding: var(--menu-dropdown-link-padding); + font-size: var(--menu-dropdown-link-font-size); + line-height: $font-height-lg; + font-weight: var(--menu-dropdown-link-font-weight); + + @include media-up(md) { + vertical-align: top; + } + + .has-dropdown & { + --menu-dropdown-link-indent: #{$horizontal-padding-mobile}; + } + } + + &-opener { + height: var(--menu-dropdown-opener-height); + width: var(--menu-dropdown-opener-width); + max-width: $max-width; + display: var(--menu-dropdown-opener-display); + align-items: center; + justify-content: var(--menu-dropdown-opener-position); + font-weight: var(--menu-dropdown-opener-weight); + cursor: pointer; + transition: all $transition-duration $transition-function; + font-size: 18px; + grid-area: item-2; + position: relative; + margin: 0 auto; + z-index: 1; + color: var(--menu-opener-color); + + @include media-up(md) { + transform: var(--menu-dropdown-opener-transform); + } + + @include hover { + --menu-opener-color: #{$color-outline}; + } + + path { + @include hover { + --menu-opener-color: #{$color-outline}; + } + } + } + + &-wrapper { + position: var(--menu-dropdown-wrapper-position); + top: 0; + left: 0; + width: var(--menu-dropdown-wrapper-width); + height: var(--menu-dropdown-wrapper-height); + max-height: calc(100vh - $header-height); + max-width: $max-width; + margin: var(--menu-dropdown-wrapper-margin); + padding: var(--menu-dropdown-wrapper-padding); + background: $background-primary; + overflow: var(--menu-dropdown-wrapper-overflow); + transform: var(--menu-dropdown-wrapper-transform); + transition: transform $transition-duration $transition-function; + border-bottom: 1px solid $color-border; + } + + #{&} { + --menu-dropdown-transform: translateX(100%); + + @include media-up(md) { + --menu-dropdown-position: static; + --menu-dropdown-transform: translateX(0); + --menu-dropdown-width: 100%; + } + + &-wrapper { + .menu { + &__dropdown { + &-wrapper { + @include media-up(md) { + --menu-dropdown-wrapper-transform: none; + + border-color: transparent; + } + } + + &-link { + .has-dropdown & { + @include media-up(md) { + --menu-dropdown-link-indent: 0; + } + } + } + + &-opener { + @include media-up(md) { + --menu-dropdown-opener-display: none; + } + } + } + } + } + } + + > #{&} { + &-wrapper { + > .menu__dropdown-opener { + --menu-dropdown-opener-position: flex-start; + --menu-dropdown-opener-weight: #{$font-weight-semibold}; + --menu-dropdown-opener-width: auto; + + .menu__item-icon { + --menu-item-icon-transform: rotate(90deg); + --menu-item-icon-margin: 0 10px -1px 0; + } + } + + [id^="header-dropdown-trigger-menu"]:checked ~ &, + [id*="header-dropdown-trigger-menu"]:checked ~ & { + --menu-dropdown-wrapper-transform: translateX(0); + } + + [id^="header-dropdown-trigger-childmenu"]:checked ~ &, + [id*="header-dropdown-trigger-childmenu"]:checked ~ & { + --menu-dropdown-wrapper-transform: translateX(0); + --menu-dropdown-wrapper-overflow: clip auto; + } + } + } + + [id^="header-dropdown-trigger-childmenu"]:checked ~ &, + [id*="header-dropdown-trigger-childmenu"]:checked ~ &, + [id^="header-dropdown-trigger-menu"]:checked ~ &, + [id*="header-dropdown-trigger-menu"]:checked ~ & { + --menu-dropdown-transform: translateX(0); + --menu-dropdown-events: all; + } + } + + &:has([id^="header-dropdown-trigger-menu"]:checked), + &:has([id*="header-dropdown-trigger-menu"]:checked) { + /* stylelint-disable-next-line */ + .menu__nav { + --menu-nav-overflow: clip; + } + } + + &:has([id^="header-dropdown-trigger-childmenu"]:checked), + &:has([id*="header-dropdown-trigger-childmenu"]:checked) { + .menu__item { + > .menu__dropdown { + > .menu__dropdown-wrapper { + --menu-dropdown-wrapper-overflow: clip; + } + } + } + } + + .header__btn-wrapper { + display: var(--menu-display); + + @include media-up(md) { + --menu-display: none; + } + } + + .social-networks { + @include media-up(md) { + display: none; + } + } +} diff --git a/src/scss/_components/_search.scss b/src/scss/_components/_search.scss new file mode 100644 index 0000000..22934b6 --- /dev/null +++ b/src/scss/_components/_search.scss @@ -0,0 +1,285 @@ +// Import variables +@use '../variables' as *; + +// Import mixins +@use '../_helpers/mixins' as *; + +.search { + --search-dropdown-delay: 50ms; + --search-dropdown-duration: #{$transition-duration}; + --search-dropdown-events: none; + --search-dropdown-padding: #{$horizontal-padding-mobile}; + --search-dropdown-transform: translateY(-100%); + --search-dropdown-visibility: hidden; + --search-input-width: 100%; + --search-icon-color: currentcolor; + --search-icon-display: block; + --search-icon-label-color: currentcolor; + --search-icon-form-width: 100%; + --search-icon-rotate: 45deg; + --search-input-border-color: #{$color-border}; + --search-input-font-size: inherit; + --search-input-min-width: 230px; + --search-input-padding-left: 46px; + --search-input-padding-right: 55px; + + @include media-up(md) { + --search-dropdown-padding: #{$horizontal-padding}; + --search-icon-form-width: auto; + } + + g { + pointer-events: none; + stroke: var(--search-icon-color); + transition: stroke $transition-duration $transition-function; + } + + &__opener { + height: $button-height; + width: $button-width; + border-radius: $button-border-radius; + display: flex; + align-items: center; + justify-content: center; + background: $button-background; + position: relative; + cursor: pointer; + + svg { + pointer-events: none; + } + + @include hover { + g { + --search-icon-color: #{$color-outline}; + } + } + } + + &__closer { + text-align: center; + margin-top: 16px; + display: flex; + align-items: center; + justify-content: center; + font-size: 14px; + cursor: pointer; + + &-icon { + position: relative; + display: block; + width: 20px; + height: 20px; + + &:not(:only-child) { + margin-left: 8px; + } + + &::before, + &::after { + content: ""; + position: absolute; + top: 50%; + left: 50%; + height: 1px; + width: 12px; + background: var(--search-icon-label-color); + transform: translate(-50%, -50%) rotate(var(--search-icon-rotate)); + transition: background $transition-duration $transition-function; + } + + &::after { + --search-icon-rotate: -45deg; + } + } + + &-label { + color: var(--search-icon-label-color); + transition: color $transition-duration $transition-function; + } + + @include hover { + .search__closer-label { + --search-icon-label-color: #{$color-outline}; + } + + .search__closer-icon { + &::before, + &::after { + --search-icon-label-color: #{$color-outline}; + } + } + } + } + + &__wrapper { + position: absolute; + width: 100vw; + height: auto; + top: 0; + left: 0; + max-width: $max-width; + margin: 0 auto; + transform: var(--search-dropdown-transform); + transition: transform $transition-duration $transition-function, + height $transition-duration $transition-function; + display: flex; + flex-direction: column; + align-items: center; + overflow: clip; + color: currentcolor; + background: $background-primary; + padding: 16px var(--search-dropdown-padding); + flex: 0 1 auto; + + form { + position: relative; + width: var(--search-icon-form-width); + } + } + + &__holder { + position: absolute; + height: calc(100dvh - $header-height); + width: 100%; + top: $header-height; + left: 0; + overflow: clip; + pointer-events: var(--search-dropdown-events); + visibility: var(--search-dropdown-visibility); + transition: visibility var(--search-dropdown-duration) $transition-function var(--search-dropdown-delay); + display: flex !important; + + #header-opener-search:checked ~ & { + --search-dropdown-events: all; + --search-dropdown-duration: 0s; + --search-dropdown-delay: 0s; + --search-dropdown-visibility: visible; + + .search__wrapper { + --search-dropdown-transform: translateY(0); + } + } + } + + &__reset { + position: absolute; + right: 7px; + top: 50%; + height: $button-height; + width: $button-width; + visibility: hidden; + transform: translate(0, -50%); + transition: visibility $transition-duration $transition-function; + display: flex; + align-items: center; + background: $button-background; + border-radius: $button-border-radius; + cursor: pointer; + + .filled + & { + visibility: visible; + } + + &::before, + &::after { + content: ""; + position: absolute; + top: 50%; + left: 50%; + height: 2px; + width: 18px; + transform: translate(-50%, -50%) rotate(var(--search-icon-rotate)); + background: var(--search-icon-color); + transition: background $transition-duration $transition-function; + } + + &::after { + --search-icon-rotate: -45deg; + } + + @include hover { + &::before, + &::after { + --search-icon-color: #{$color-outline}; + } + } + } + + &__icon { + font-size: 20px; + width: 20px; + height: 20px; + pointer-events: none; + position: absolute; + top: 50%; + left: 13px; + transform: translateY(-65%); + z-index: 1; + display: var(--search-icon-display); + } + + &__input { + min-width: var(--search-input-min-width); + width: var(--search-input-width); + padding: 14px var(--search-input-padding-right) 14px var(--search-input-padding-left); + font-size: var(--search-input-font-size); + font-weight: $font-weight-bold; + line-height: $font-height-sm; + border: 1px solid var(--search-input-border-color); + border-radius: $button-border-radius; + color: currentcolor; + background: $background-primary; + + @include media-up(md) { + --search-input-border-color: transparent; + --search-input-font-size: 20px; + --search-input-min-width: auto; + --search-input-padding-left: 5px; + --search-input-padding-right: 60px; + --search-input-width: auto; + } + + &-wrapper { + position: relative; + flex: 1 1 auto; + + .search__icon { + @include media-up(md) { + --search-icon-display: none; + } + } + } + + &::placeholder { + color: $color-placeholder; + } + + &::-webkit-search-cancel-button { + display: none !important; + } + + &::-webkit-search-decoration { + display: none; + } + + &:focus { + outline: none; + } + + &:-webkit-autofill, + &:autofill { + box-shadow: inset 0 0 0 150px $background-primary !important; + } + } + + &:focus-within { + /* stylelint-disable-next-line */ + .search__holder { + --search-dropdown-delay: 0s; + --search-dropdown-duration: 0s; + --search-dropdown-events: all; + --search-dropdown-visibility: visible; + } + } +} diff --git a/src/scss/_components/_socials.scss b/src/scss/_components/_socials.scss new file mode 100644 index 0000000..2581816 --- /dev/null +++ b/src/scss/_components/_socials.scss @@ -0,0 +1,29 @@ +// Import variables +@use '../variables' as *; + +// Import mixins +@use '../_helpers/mixins' as *; + +.social-networks { + $social-size: var(--social-size); + $social-icon-spacing: var(--social-icon-spacing); + + display: flex; + flex-wrap: wrap; + margin: 0 calc(-1 * $social-icon-spacing) calc(-1 * $social-icon-spacing); + font-size: $social-size; + + @include media-up(md) { + justify-content: flex-end; + } + + &__item { + margin: 0 $social-icon-spacing $social-icon-spacing; + } + + &__link { + @include hover { + opacity: 1; + } + } +} diff --git a/src/scss/_helpers/_mixins.scss b/src/scss/_helpers/_mixins.scss index 88651f5..a0a153e 100644 --- a/src/scss/_helpers/_mixins.scss +++ b/src/scss/_helpers/_mixins.scss @@ -84,3 +84,17 @@ $breakpoints: ( } } } + +// Hover mixin using :hover pseudo-class +@mixin hover { + &:hover { + @content; + } +} + +// Reduced motion mixin for accessibility +@mixin reduced-motion { + @media (prefers-reduced-motion: reduce) { + @content; + } +} diff --git a/src/scss/_variables.scss b/src/scss/_variables.scss index 4c0dd9b..4ed498f 100644 --- a/src/scss/_variables.scss +++ b/src/scss/_variables.scss @@ -11,11 +11,12 @@ $breakpoint-xxl: 1920px; // Colors $background-accent: var(--background-accent); $background-primary: var(--background-primary, #fff); +$background-shadow: color-mix(in srgb, var(--color-primary) 55%, transparent); $color-accent: var(--color-accent); -$color-border: var(--color-border); +$color-border: color-mix(in srgb, var(--color-primary) 15%, transparent); $color-outline: var(--color-outline); +$color-placeholder: color-mix(in srgb, var(--color-primary) 50%, transparent); $color-primary: var(--color-primary, #000); -$color-placeholder: var(--color-placeholder); $color-secondary: var(--color-secondary); // Fonts @@ -24,9 +25,6 @@ $font-family-heading: var(--font-heading); $font-family-tagline: var(--font-tagline); // Sizes -$max-width: 1920px; -$horizontal-padding: 80px; -$horizontal-padding-mobile: 16px; $font-size-base: var(--font-size-base); $font-size-button: var(--font-size-button); $font-size-h1: var(--font-size-h1); @@ -36,13 +34,17 @@ $font-size-h4: var(--font-size-h4); $font-size-h5: var(--font-size-h5); $font-size-h6: var(--font-size-h6); $font-size-tagline: var(--font-size-tagline); +$header-height: var(--header-height, 65px); +$horizontal-padding: 80px; +$horizontal-padding-mobile: 16px; +$max-width: 1920px; // Line heights -$font-height-xs: var(--font-height-xs); -$font-height-sm: var(--font-height-sm); -$font-height-md: var(--font-height-md); -$font-height-lg: var(--font-height-lg); -$font-height-xl: var(--font-height-xl); +$font-height-xs: 1; +$font-height-sm: 1.25; +$font-height-md: 1.3333; +$font-height-lg: 1.4; +$font-height-xl: 1.5; // Weights $font-weight-button: var(--font-weight-button); @@ -57,13 +59,16 @@ $font-weight-semibold: var(--font-weight-semibold); $font-weight-bold: var(--font-weight-bold); $font-weight-extrabold: var(--font-weight-extrabold); -// Spacing -$padding-button: var(--padding-button); - // Border radius $border-radius: var(--border-radius); -$border-radius-button: var(--border-radius-button); + +// Buttons +$button-border-radius: var(--button-border-radius); +$button-background: color-mix(in srgb, var(--color-primary) 15%, transparent); +$button-padding: var(--button-padding); +$button-height: var(--button-height); +$button-width: var(--button-width); // Transition $transition-duration: var(--transition-duration); -$transition-function: ease-in-out; +$transition-function: var(--transition-function); diff --git a/src/scss/critical.scss b/src/scss/critical.scss index b5e7542..a7659d5 100644 --- a/src/scss/critical.scss +++ b/src/scss/critical.scss @@ -7,7 +7,7 @@ // Import styles @use '_base/forms' as *; @use '_base/typography' as *; - +@use '_components/logo' as *; *, *::before, @@ -28,6 +28,7 @@ html { body { display: flex; flex-direction: column; + position: relative; color: $color-primary; background: $background-primary; margin: 0; diff --git a/src/scss/header.scss b/src/scss/header.scss new file mode 100644 index 0000000..044d510 --- /dev/null +++ b/src/scss/header.scss @@ -0,0 +1,226 @@ +// Import variables +@use 'variables' as *; + +// Import mixins +@use '_helpers/mixins' as *; + +// Import components +@use '_components/menu' as *; +@use '_components/search' as *; + +.header { + --header-background: transparent; + --header-border-color: #{$color-border}; + --header-btn-display: none; + --header-content-gap: 8px; + --header-logo-visibility: visible; + --header-position: static; + --header-transition-duration: #{$transition-duration}; + --header-transition-delay: #{$transition-duration}; + + z-index: 1; + position: var(--header-position); + top: 0; + left: 0; + right: 0; + + @include media-up(md) { + --header-btn-display: block; + } + + &__inner { + --header-logo-grow: 1; + --header-menu-grow: 0; + --header-menu-bottom-padding: 0; + --header-menu-indent: var(--menu-indent-mobile); + --header-padding-bottom: var(--padding-bottom-mobile, 12px); + --header-padding-top: var(--padding-top-mobile, 12px); + --header-order-app: var(--order-app-mobile, 2); + --header-order-button: var(--order-button-mobile, 6); + --header-order-cart: var(--order-cart-mobile, 4); + --header-order-logo: var(--order-logo-mobile, 1); + --header-order-menu: var(--order-menu-mobile, 5); + --header-order-search: var(--order-search-mobile, 3); + + color: $color-primary; + background: var(--header-background); + border-bottom: 1px solid var(--header-border-color); + transition: background var(--header-transition-duration) var(--header-transition-delay) $transition-function, + border var(--header-transition-duration) var(--header-transition-delay) $transition-function, + padding $transition-duration $transition-function; + + @include media-up(md) { + --header-logo-grow: var(--logo-growth-desktop, 1); + --header-menu-grow: var(--menu-growth-desktop, 1); + --header-menu-bottom-padding: var(--menu-bottom-padding-top-desktop); + --header-menu-bottom-border-top-width: var(--menu-bottom-border-top-width-desktop, 100%); + --header-menu-indent: var(--menu-indent-desktop); + --header-padding-bottom: var(--padding-bottom-desktop, 12px); + --header-padding-top: var(--padding-top-desktop, 12px); + --header-order-app: var(--order-app-desktop, 2); + --header-order-button: var(--order-button-desktop, 5); + --header-order-cart: var(--order-cart-desktop, 4); + --header-order-logo: var(--order-logo-desktop, 1); + --header-order-menu: var(--order-menu-desktop, 6); + --header-order-search: var(--order-search-desktop, 3); + } + + &--padding-top { + padding-top: var(--header-padding-top); + } + + &--padding-bottom { + padding-bottom: var(--header-padding-bottom); + } + + &--background { + background: $background-primary; + } + } + + &__content { + display: flex; + justify-content: space-between; + align-items: stretch; + + @include media-up(md) { + flex-wrap: wrap; + } + + &--outlined { + @include media-up(md) { + border: 1px solid $color-border; + border-radius: $border-radius; + padding: var(--header-padding-top) var(--header-padding-bottom) var(--header-padding-bottom); + margin: calc(-1 * var(--header-padding-top)) 0 calc(-1 * var(--header-padding-bottom)); + } + } + } + + &__menu { + flex: var(--header-menu-grow) 1 auto; + order: var(--header-order-menu); + margin: var(--header-menu-indent); + + @include media-up(md) { + width: var(--menu-bottom-width-desktop, 100%); + position: relative; + padding: var(--header-menu-bottom-padding); + } + + &::before { + @include media-up(md) { + content: ""; + display: block; + width: calc(var(--header-menu-bottom-border-top-width)); + height: var(--menu-border-width-desktop, 1px); + margin: 0; + position: absolute; + left: 50%; + top: 0; + transform: translateX(-50%); + background: $color-border; + } + } + } + + &__logo { + flex: var(--header-logo-grow) 1 auto; + order: var(--header-order-logo); + padding-right: 16px; + + &-image { + &-holder { + transition: all var(--header-transition-duration) var(--header-transition-delay) $transition-function; + visibility: var(--header-logo-visibility); + } + } + } + + &__search { + flex: 0 1 auto; + order: var(--header-order-search); + padding-left: var(--header-content-gap); + } + + &__cart { + order: var(--header-order-cart); + flex: 0 1 auto; + padding-left: var(--header-content-gap); + } + + &__btn { + &-wrapper { + flex: 0 1 auto; + display: var(--header-btn-display); + + @include media-up(md) { + order: var(--header-order-button); + padding-left: var(--header-content-gap); + } + } + } + + &:has(.header__inner--sticky) { + --header-position: sticky; + } + + &:has(.header__content--outlined) { + --header-border-color: transparent; + } + + &:not(&:has(.header__inner--background)) { + --header-border-color: transparent; + --header-position: absolute; + + .header__logo-image-holder { + --header-logo-visibility: hidden; + + &--floated { + --header-logo-visibility: visible; + } + + &:only-child { + --header-logo-visibility: visible; + } + } + + &:has(.header__inner--sticky) { + --header-position: fixed; + } + } + + &:has(.menu__item.has-dropdown:hover), + &:has(#header-opener-menu:checked), + &:has(#header-opener-search:checked){ + --header-border-color: #{$color-border}; + --header-transition-duration: 0s; + --header-transition-delay: 0s; + + .header__inner { + --header-background: #{$background-primary}; + --header-transition-duration: 0s; + --header-transition-delay: 0s; + + .header__logo-image-holder { + --header-logo-visibility: visible; + + &--floated { + --header-logo-visibility: hidden; + + /* stylelint-disable-next-line */ + &:only-child { + --header-logo-visibility: visible; + } + } + } + } + + /* stylelint-disable-next-line */ + &:has(.header__content--outlined) { + @include media-up(md) { + --header-border-color: transparent; + } + } + } +} diff --git a/src/scss/styles.scss b/src/scss/styles.scss index 5225fd4..37db7ec 100644 --- a/src/scss/styles.scss +++ b/src/scss/styles.scss @@ -5,3 +5,4 @@ @use '_base/base' as *; @use '_base/rx' as *; @use '_components/button' as *; +@use '_components/socials' as *; diff --git a/src/sections/header.liquid b/src/sections/header.liquid new file mode 100644 index 0000000..7a97f6c --- /dev/null +++ b/src/sections/header.liquid @@ -0,0 +1,581 @@ +{%- assign app = section.blocks | where: "type", "app" | first -%} +{%- assign block_id = '' -%} +{%- assign color_scheme = section.settings.color_scheme -%} +{%- assign header_outline = section.settings.header_outline -%} +{%- assign menu_alignment_horizontal = '' -%} +{%- assign menu_alignment_vertical = '' -%} +{%- assign menu_opener_position = '' -%} +{%- assign padding_bottom_desktop = section.settings.padding_bottom_desktop -%} +{%- assign padding_bottom_mobile = section.settings.padding_bottom_mobile -%} +{%- assign padding_top_desktop = section.settings.padding_top_desktop -%} +{%- assign padding_top_mobile = section.settings.padding_top_mobile -%} +{%- assign section_type = section.type -%} +{%- assign show_button_desktop = '' -%} +{%- assign show_button_mobile = '' -%} +{%- assign show_transparency = section.settings.show_transparency -%} +{%- assign sticky_header = section.settings.sticky_header -%} + +{%- for block in section.blocks -%} + {%- case block.type -%} + {%- when 'menu' -%} + {%- assign block_id = block.id -%} + {%- assign menu_alignment_horizontal = block.settings.menu_alignment_horizontal -%} + {%- assign menu_alignment_vertical = block.settings.menu_alignment_vertical -%} + {%- assign menu_opener_position = block.settings.menu_opener_position -%} + + {%- when 'button' -%} + {%- assign button_label = block.settings.button_label -%} + {%- assign button_url = block.settings.button_url -%} + {%- assign show_button_desktop = block.settings.show_button_desktop -%} + {%- assign show_button_mobile = block.settings.show_button_mobile -%} + + {%- if button_label != blank and button_url != blank -%} + {%- capture element_button -%} + {%- render 'button' + button_label: button_label, + button_type: 'primary', + button_url: button_url, + section_type: section_type + -%} + {%- endcapture -%} + {%- endif -%} + {%- endcase -%} +{%- endfor -%} + +{%- if app -%} + {%- capture element_app_desktop -%} +
+ {%- render 'app', block: app, section: section, size: "desktop" -%} +
+ {%- endcapture -%} +{%- endif -%} + +{% comment %} CSS variables start {% endcomment %} +{%- capture variables -%} + {%- case padding_bottom_desktop -%} + {%- when 'md' -%} + --padding-bottom-desktop: 16px; + {%- when 'lg' -%} + --padding-bottom-desktop: 20px; + {%- endcase -%} + + {%- case padding_bottom_mobile -%} + {%- when 'md' -%} + --padding-bottom-mobile: 16px; + {%- when 'lg' -%} + --padding-bottom-mobile: 20px; + {%- endcase -%} + + {%- case padding_top_desktop -%} + {%- when 'md' -%} + --padding-top-desktop: 16px; + {%- when 'lg' -%} + --padding-top-desktop: 20px; + {%- endcase -%} + + {%- case padding_top_mobile -%} + {%- when 'md' -%} + --padding-top-mobile: 16px; + {%- when 'lg' -%} + --padding-top-mobile: 20px; + {%- endcase -%} + + {%- if menu_opener_position == 'left' -%} + --menu-indent-mobile: 0 20px 0 0; + --order-app-mobile: 3; + --order-button-mobile: 6; + --order-cart-mobile: 5; + --order-logo-mobile: 2; + --order-menu-mobile: 1; + --order-search-mobile: 4; + {%- else -%} + --menu-indent-mobile: 0 0 0 8px; + {%- endif -%} + + {%- if menu_alignment_vertical == 'top' -%} + --logo-growth-desktop: 0; + --menu-growth-desktop: 1; + --menu-border-width-desktop: 0; + --menu-bottom-border-top-width-desktop: 0; + --menu-bottom-padding-top-desktop: 0; + --menu-bottom-width-desktop: auto; + --menu-margin-x-desktop: auto; + --menu-indent-desktop: 0 20px; + --order-app-desktop: 3; + --order-button-desktop: 6; + --order-cart-desktop: 5; + --order-logo-desktop: 1; + --order-menu-desktop: 2; + --order-search-desktop: 4; + {%- else -%} + --menu-bottom-border-top-width-desktop: 100vw; + {%- if header_outline == "bordered" -%} + --menu-bottom-margin-desktop: {% if padding_bottom_desktop == 'lg' %}20px{% elsif padding_bottom_desktop == 'md' %}16px{% else %}12px{% endif %}; + --menu-bottom-border-top-width-desktop: calc(100% + var(--menu-bottom-margin-desktop) * 2); + {%- endif -%} + --menu-bottom-padding-top-desktop: {% if padding_bottom_desktop == 'lg' %}20px{% elsif padding_bottom_desktop == 'md' %}16px{% else %}12px{% endif %} 0 0; + --menu-margin-x-desktop: -22px; + --menu-indent-desktop: {% if padding_bottom_desktop == 'lg' %}20px{% elsif padding_bottom_desktop == 'md' %}16px{% else %}12px{% endif %} 0 0; + {%- endif -%} + + {%- if menu_alignment_horizontal == 'left' -%} + --menu-horizontal-position-desktop: flex-start; + {%- elsif menu_alignment_horizontal == 'right' -%} + --menu-horizontal-position-desktop: flex-end; + {%- endif -%} +{%- endcapture -%} +{% comment %} CSS variables end {% endcomment %} + +{%- for block in section.blocks -%} + {%- case block.type -%} + {%- when 'logo' -%} + {%- capture element_logo -%} + {%- assign logo = block.settings.logo_image -%} + {%- assign logo_floated = block.settings.logo_image_floated -%} + {%- assign logo_title = block.settings.logo_custom_title -%} + {%- assign svg = block.settings.logo_svg -%} + {%- assign svg_floated = block.settings.logo_svg_floated -%} + + {%- render 'logo' + logo: logo, + logo_floated: logo_floated, + logo_title: logo_title, + loading: 'eager', + routes: routes, + section_type: section_type, + shop: shop, + svg: svg, + svg_floated: svg_floated, + transparency: show_transparency + -%} + {%- endcapture -%} + + {%- when 'menu' -%} + {%- assign menu = block.settings.menu -%} + {%- assign menu_back_label = block.settings.menu_back_label -%} + + {%- if menu != blank -%} + {%- if app -%} + {%- capture element_app_mobile -%} +
+ {%- render 'app', block: app, section: section, size: "mobile" -%} +
+ {%- endcapture -%} + {%- endif -%} + + {%- capture element_menu_content -%} + {%- if show_button_mobile -%} + {{- element_button -}} + {%- endif -%} + {{- element_app_mobile -}} + {%- endcapture -%} + + {%- capture element_menu -%} + {%- render 'menu' + content: element_menu_content, + menu: menu, + menu_back_label: menu_back_label, + menu_id: block_id, + menu_levels: 3, + section_type: section_type, + settings: settings + -%} + {%- endcapture -%} + {%- endif -%} + + {%- when 'search' -%} + {%- assign show_search = block.settings.show_search -%} + + {%- if show_search -%} + {%- capture element_search -%} + {%- assign search_closer_label = block.settings.search_closer_label -%} + {%- assign search_placeholder = block.settings.search_placeholder -%} + + {%- render 'search', + closer_label: search_closer_label, + placeholder: search_placeholder, + routes: routes, + section_type: section_type + -%} + {%- endcapture -%} + {%- endif -%} + + {%- endcase -%} +{%- endfor -%} + + + +{% schema %} + { + "name": "Header", + "tag": "section", + "class": "header__wrapper", + "templates": [], + "layout": true, + "settings": [ + { + "type": "header", + "content": "General settings" + }, + { + "type": "checkbox", + "id": "sticky_header", + "default": true, + "label": "Stick to top" + }, + { + "type": "checkbox", + "id": "show_transparency", + "default": true, + "label": "Use transparent background" + }, + { + "type": "color_scheme", + "id": "color_scheme", + "label": "Color scheme", + "default": "set-1" + }, + { + "type": "header", + "content": "Desktop settings" + }, + { + "type": "select", + "id": "padding_top_desktop", + "label": "Padding top", + "options": [ + { + "value": "sm", + "label": "Small" + }, + { + "value": "md", + "label": "Medium" + }, + { + "value": "lg", + "label": "Large" + } + ], + "default": "large" + }, + { + "type": "select", + "id": "padding_bottom_desktop", + "label": "Padding bottom", + "options": [ + { + "value": "sm", + "label": "Small" + }, + { + "value": "md", + "label": "Medium" + }, + { + "value": "lg", + "label": "Large" + } + ], + "default": "large" + }, + { + "type": "select", + "id": "header_outline", + "label": "Header outline", + "options": [ + { + "value": "none", + "label": "None" + }, + { + "value": "bordered", + "label": "Bordered" + } + ], + "default": "none" + }, + { + "type": "header", + "content": "Mobile settings" + }, + { + "type": "select", + "id": "padding_top_mobile", + "label": "Padding top", + "options": [ + { + "value": "sm", + "label": "Small" + }, + { + "value": "md", + "label": "Medium" + }, + { + "value": "lg", + "label": "Large" + } + ], + "default": "medium" + }, + { + "type": "select", + "id": "padding_bottom_mobile", + "label": "Padding bottom", + "options": [ + { + "value": "sm", + "label": "Small" + }, + { + "value": "md", + "label": "Medium" + }, + { + "value": "lg", + "label": "Large" + } + ], + "default": "medium" + } + ], + "blocks": [ + { + "type": "logo", + "name": "Logo", + "limit": 1, + "settings": [ + { + "type": "header", + "content": "General settings" + }, + { + "type": "textarea", + "id": "logo_svg", + "label": "SVG logo" + }, + { + "type": "image_picker", + "id": "logo_image", + "label": "Image logo", + "info": "Displayed if no SVG code is added" + }, + { + "type": "header", + "content": "Optional settings" + }, + { + "type": "paragraph", + "content": "Displayed if header background is transparent" + }, + { + "type": "textarea", + "id": "logo_svg_floated", + "label": "SVG logo" + }, + { + "type": "image_picker", + "id": "logo_image_floated", + "label": "Image logo", + "info": "Displayed if no SVG code is added" + }, + { + "type": "header", + "content": "Alternative settings" + }, + { + "type": "paragraph", + "content": "Displayed when no logo is added" + }, + { + "type": "text", + "id": "logo_custom_title", + "label": "Site title" + } + ] + }, + { + "type": "menu", + "name": "Menu", + "limit": 1, + "settings": [ + { + "type": "header", + "content": "General settings" + }, + { + "type": "menu", + "id": "menu", + "label": "Menu", + "default": "main-menu" + }, + { + "type": "header", + "content": "Desktop settings" + }, + { + "type": "select", + "id": "menu_alignment_horizontal", + "label": "Horizontal position", + "options": [ + { + "value": "left", + "label": "Left" + }, + { + "value": "center", + "label": "Center" + }, + { + "value": "right", + "label": "Right" + } + ], + "default": "center" + }, + { + "type": "select", + "id": "menu_alignment_vertical", + "label": "Vertical alignment", + "options": [ + { + "value": "top", + "label": "Top" + }, + { + "value": "bottom", + "label": "Bottom" + } + ], + "default": "top" + }, + { + "type": "header", + "content": "Mobile settings" + }, + { + "type": "select", + "id": "menu_opener_position", + "label": "Opener position", + "options": [ + { + "value": "left", + "label": "Left" + }, + { + "value": "right", + "label": "Right" + } + ], + "default": "left" + }, + { + "type": "text", + "id": "menu_back_label", + "label": "'Back' button label" + } + ] + }, + { + "type": "search", + "name": "Search", + "limit": 1, + "settings": [ + { + "type": "header", + "content": "General settings" + }, + { + "type": "checkbox", + "id": "show_search", + "default": true, + "label": "Show search" + }, + { + "type": "text", + "id": "search_closer_label", + "label": "'Close' button label", + "default": "Close" + }, + { + "type": "text", + "id": "search_placeholder", + "label": "Placeholder", + "default": "Search" + } + ] + }, + { + "type": "button", + "name": "Button", + "limit": 1, + "settings": [ + { + "type": "header", + "content": "General settings" + }, + { + "type": "text", + "id": "button_label", + "label": "Label", + "default": "Contact us" + }, + { + "type": "url", + "id": "button_url", + "label": "URL" + }, + { + "type": "header", + "content": "Desktop settings" + }, + { + "type": "checkbox", + "id": "show_button_desktop", + "label": "Show/hide button", + "default": true + }, + { + "type": "header", + "content": "Mobile settings" + }, + { + "type": "checkbox", + "id": "show_button_mobile", + "label": "Show/hide button", + "default": true + } + ] + }, + { + "type": "@app" + } + ] + } +{% endschema %} diff --git a/src/snippets/button.liquid b/src/snippets/button.liquid new file mode 100644 index 0000000..7ea0b2b --- /dev/null +++ b/src/snippets/button.liquid @@ -0,0 +1,29 @@ +{%- comment -%} + This snippet renders a button with customizable label, URL, and style. + + Parameters: + - button_label: the text to display on the button + - button_url: the URL the button links to + - button_type: the style of the button (e.g., 'primary', 'outline') + - section_type: the section type where the button is included (e.g. 'header', 'footer') + + Required parameters: + - button_label + - button_url + - section_type + + Usage: + + {%- render 'button', + button_label: 'Click me', + button_url: '/your-link', + button_type: 'primary', + section_type: section.type + -%} +{%- endcomment -%} + + + + {{- button_label -}} + + diff --git a/src/snippets/css-mini-cart-button.liquid b/src/snippets/css-mini-cart-button.liquid new file mode 100644 index 0000000..8ec3c22 --- /dev/null +++ b/src/snippets/css-mini-cart-button.liquid @@ -0,0 +1,34 @@ +{%- style -%} + bq-minicart-button { + display: flex; + align-items: center; + justify-content: center; + height: var(--button-height); + width: var(--button-width); + background: color-mix(in srgb, var(--color-primary) 15%, transparent); + border-radius: var(--button-border-radius); + + --minicart-button-accent-color: var(--background-accent); + --minicart-button-badge-color: var(--color-accent); + --minicart-button-badge-size: 12px; + --minicart-button-border-radius: 50%; + --minicart-button-color: var(--color-primary); + --minicart-button-font: var(--font-body, --minicart-font, sans-serif); + --minicart-button-font-size: calc(var(--font-size-base) - 2px); + --minicart-button-hover-color: var(--color-outline); + --minicart-button-hover-opacity: 1; + --minicart-button-padding: 4px; + --minicart-button-size: 21px; + --minicart-button-transition-delay: 0ms; + --minicart-button-transition-duration: var(--transition-duration); + --minicart-button-transition-property: all; + --minicart-button-transition-timing-function: var(--transition-function); + --minicart-button-outer-background: transparent; + --minicart-button-outer-background-hover: transparent; + --minicart-button-outer-border: transparent; + --minicart-button-outer-border-radius: 0; + --minicart-button-outer-hover-border: transparent; + --minicart-button-outer-padding: 7px 3px 2px 2px; + --minicart-button-outer-transition: none; + } +{%- endstyle -%} diff --git a/src/snippets/root-variables.liquid b/src/snippets/css-root-variables.liquid similarity index 81% rename from src/snippets/root-variables.liquid rename to src/snippets/css-root-variables.liquid index 026d709..d2b18d4 100644 --- a/src/snippets/root-variables.liquid +++ b/src/snippets/css-root-variables.liquid @@ -10,9 +10,7 @@ --background-accent: {{ scheme.settings.background_accent }}; --background-primary: {{ scheme.settings.background }}; --color-accent: {{ scheme.settings.color_accent }}; - --color-border: {{ scheme.settings.color_primary | append: '26' }}; --color-outline: {{ scheme.settings.color_accent_outline }}; - --color-placeholder: {{ scheme.settings.color_primary | append: '80' }}; --color-primary: {{ scheme.settings.color_primary }}; --color-secondary: {{ scheme.settings.color_secondary }}; } @@ -21,19 +19,29 @@ :root { {% case settings.corner_radius %} {%- when 'square' -%} - --border-radius: 0; + --border-radius: 0; {%- when 'rounded' -%} - --border-radius: 8px; + --border-radius: 8px; {%- when 'fully_round' -%} - --border-radius: 50px; + --border-radius: 50px; {% endcase %} {% case settings.button_corners %} {%- when 'square' -%} - --border-radius-button: 0; + --button-border-radius: 0; {%- when 'rounded' -%} - --border-radius-button: 8px; + --button-border-radius: 8px; {%- when 'fully_round' -%} - --border-radius-button: 50px; + --button-border-radius: 50px; + {% endcase %} + --button-height: 40px; + --button-width: 40px; + {% case settings.button_size %} + {%- when 'sm' -%} + --button-padding: 5px 12px; + {%- when 'md' -%} + --button-padding: 5px 16px; + {%- when 'xl' -%} + --button-padding: 5px 20px; {% endcase %} --font-body: "{{ settings.font_body.family | default: Roboto }}", {{ settings.font_body.fallback_families | default: sans-serif }}; --font-heading: "{{ settings.font_heading.family | default: Nippo }}", {{ settings.font_heading.fallback_families | default: sans-serif }}; @@ -63,18 +71,29 @@ --font-weight-semibold: {{ settings.font_weight_semibold | default: "600" }}; --font-weight-bold: {{ settings.font_weight_bold | default: "700" }}; --font-weight-extrabold: {{ settings.font_weight_extrabold | default: "800" }}; - {% case settings.button_size %} + {%- case settings.social_icon_size -%} {%- when 'sm' -%} - --padding-button: 8px 12px; + --social-size: 18px; {%- when 'md' -%} - --padding-button: 10px 16px; - {%- when 'xl' -%} - --padding-button: 12px 20px; - {% endcase %} + --social-size: 21px; + {%- when 'lg' -%} + --social-size: 24px; + {%- endcase -%} + + {%- case settings.social_icon_spacing -%} + {%- when 'sm' -%} + --social-icon-spacing: 5px; + {%- when 'md' -%} + --social-icon-spacing: 7px; + {%- when 'lg' -%} + --social-icon-spacing: 9px; + {%- endcase -%} + {% if settings.transition_duration == "0" or settings.transition_duration == blank %} --transition-duration: 0.3s; {% else %} --transition-duration: {{ settings.transition_duration | append: "s" }}; {% endif %} + --transition-function: ease-in-out; } diff --git a/src/snippets/image.liquid b/src/snippets/image.liquid new file mode 100644 index 0000000..37db151 --- /dev/null +++ b/src/snippets/image.liquid @@ -0,0 +1,383 @@ +{%- comment -%} + + This snippet is used for rendering the images with optimal performance + + Parameters: + - image: the image object to render + - image_alt: alt text for the image + - image_class: a CSS class for the image + - image_mobile: the image that should be rendered on mobile devices + - image_size: predefined image size based on its expected width on the screen (default is 'm') + - custom_height: custom image height if predefined height doesn't fit + - custom_size: custom image size if predefined sizes don't fit + - custom_width: custom image width if predefined width doesn't fit + - fetchpriority: fetch priority for the image (default is 'auto') + - focal: focal point for the product images + - keep_type: whether to keep the original png or jpeg image type (intended for the store logo) + - loading: loading strategy for the image (default is 'lazy') + - settings: theme settings + - use_ratio: whether to use aspect ratio for the collection and product images (default is true) + + Required parameters: + - image + - image_class + - image_size OR custom_size and custom_width OR custom_size and custom_height + - settings + + Usage: + + {%- render 'image', + image: image, + image_mobile: image_mobile, + image_alt: 'image_alt', + image_class: 'your_class', + image_size: 'm', <-------- use only this ONE property + custom_size: '170px', <---- or these TWO properties ('custom_size' and 'custom_width' OR 'custom_size' and 'custom_height') + custom_width: '170', <---- instead of 'image_size' property + custom_height: '170', + focal: points, + loading: 'eager', + use_ratio: - true, + fetchpriority: 'high', - use for hero images or important visible content + keep_type: true, - image type will be kept (only png and jpeg) + settings: settings + -%} + + Explanation how to use 'image_size' property. + Image_size ensures we load the correct image size based on its width on the screen. + Media queries are built relative to the viewport width, so we define image size depending on expected width relative to the viewport. + + For example: + A hero image would be approximately as wide as the whole viewport, so we would use the 'xxl' size. + A testimonial image would be approximately as wide as half of the viewport (as it is show on the side), so we would use the 'l' size. + + xs - Image is wide as 1/16 of the viewport + s - Image is wide as 1/8 of the viewport + m - Image is wide as 1/4 of the viewport + l - Image is wide as half of the viewport + xl - Image is wide as 3/4 of the viewport + xxl - Image is as wide as the whole viewport + flexible - Image is wide as the viewport on small screens but is smaller relative to the screen on larger screens. + logo - Image has the same size and quality on all resolutions + +{%- endcomment -%} + +{%- assign image_url = image.url -%} +{%- assign image_url_mobile = image_mobile.url -%} + +{%- if image_url != blank or image_url_mobile != blank -%} + {%- assign breakpoint_md = '1024' | plus: 0 -%} + {%- assign breakpoint_sm = '768' | plus: 0 -%} + {%- assign dynamic_url = image_url -%} + {%- assign featured_type = '' -%} + {%- assign featured_types = 'webp, jpg, jpeg, png' | split: ', ' -%} + {%- assign fetch_priority = fetchpriority | default: 'auto' -%} + {%- assign has_dimensions = false -%} + {%- assign inner_height = image.height -%} + {%- assign inner_width = image.width -%} + {%- assign image_alt = image_alt | default: image.alt | default: '' -%} + {%- assign image_fit = settings.card_image_fit -%} + {%- assign image_main_class = image_class | append: ' image-main' -%} + {%- assign image_use_focal = settings.use_focal_images -%} + {%- assign loading_strategy = loading | default: 'lazy' -%} + {%- assign placeholder_height = '100%' -%} + + {%- if use_ratio == false -%} + {%- assign set_ratio = false -%} + {%- else -%} + {%- assign set_ratio = true -%} + {%- endif -%} + + {%- if loading_strategy == 'lazy' -%} + {%- assign placeholder_width = '25' | plus: 0 -%} + {%- assign reduction_factor = inner_width | divided_by: placeholder_width | plus: 0.0 -%} + {%- endif -%} + + {%- if image_url_mobile != blank -%} + {%- assign dynamic_url = image_url_mobile -%} + {%- endif -%} + + {%- if inner_width and inner_height -%} + {%- assign has_dimensions = true -%} + {%- endif -%} + + {% comment %} use native image dimensions to generate optimal sizes {% endcomment %} + {%- if has_dimensions -%} + {%- assign breakpoints = '' -%} + {%- assign paths = '' -%} + {%- assign sizes = '320, 425, 535, 750, 810, 1024, 1200, 1440, 1920, 2500' | split: ', ' -%} + {%- assign sizes_map = '' -%} + {%- assign source_map = '' -%} + {%- assign threshold = '' -%} + + {%- if loading_strategy == 'lazy' -%} + {%- assign placeholder_height = inner_height | divided_by: reduction_factor | round: 0 -%} + {%- endif -%} + + {% comment %} filter out sizes larger than the original image to avoid upscaling {% endcomment %} + {%- for item in sizes -%} + {%- assign size = item | plus: 0 -%} + + {%- if inner_width >= size -%} + {%- assign threshold = size -%} + {%- assign sizes_map = sizes_map | append: size | append: ', ' -%} + {%- continue -%} + {%- endif -%} + {%- endfor -%} + + {% comment %} clean up trailing signs {% endcomment %} + {%- assign sizes = '' -%} + {%- for item in sizes_map -%} + {%- assign size = item -%} + {%- if forloop.last -%} + {%- assign size = item | replace_last: ', ', '' -%} + {%- endif -%} + {%- assign sizes = sizes | append: size -%} + {%- endfor -%} + + {% comment %} generate srcset paths and responsive breakpoints {% endcomment %} + {%- assign sizes_map = sizes | split: ', ' -%} + {%- for item in sizes_map -%} + {%- unless forloop.last -%} + {%- assign comma = ', ' -%} + {%- else -%} + {%- assign comma = '' -%} + {%- endunless -%} + + {%- assign breakpoint = '' -%} + {%- assign size = item | plus: 0 -%} + {%- assign url = dynamic_url | image_url: width: size -%} + {%- assign path = url | append: ' ' | append: size | append: 'w' -%} + {%- assign paths = paths | append: path | append: comma -%} + {%- assign point = item | append: 'px' -%} + + {% comment %} add media query for sizes up to medium breakpoint {% endcomment %} + {%- if size <= breakpoint_md -%} + {%- assign breakpoint = '(max-width: ' | append: point | append: ') ' -%} + {%- endif -%} + + {%- assign breakpoints = breakpoints | append: breakpoint | append: point | append: comma -%} + {%- endfor -%} + + {% comment %} add original image size if not already included in filtered sizes {% endcomment %} + {%- if threshold != inner_width -%} + {%- assign img_points = '' -%} + {%- assign img_size = inner_width | append: 'px' -%} + {%- assign img_path = image_url | image_url: width: inner_width | append: ' ' | append: inner_width | append: 'w' -%} + {%- assign sizes = sizes | append: ', ' | append: inner_width -%} + {%- assign paths = paths | append: ', ' | append: img_path -%} + {%- if threshold <= breakpoint_md -%} + {%- assign img_points = '(max-width: ' | append: img_size | append: ') ' -%} + {%- endif -%} + {%- assign breakpoints = breakpoints | append: ', ' | append: img_points | append: img_size -%} + {%- endif -%} + + {%- assign source_map = sizes | split: ', '-%} + {% comment %} calculate aspect ratio for native CSS aspect-ratio property {% endcomment %} + {%- assign aspect_ratio = inner_width | times: 1.0 | divided_by: inner_height | round: 4 -%} + {%- else -%} + {%- assign image_size = image_size | default: 'm' -%} + + {%- case image_size -%} + {%- when 'xs' -%} + {%- assign sizes = '20, 30, 48, 75, 105, 160' -%} + {%- assign points = '(max-width: 320px) 20px, (max-width: 480px) 30px, (max-width: 768px) 48px, (max-width: 1200px) 75px, (max-width: 1680px) 105px, 160px' -%} + {%- assign width = '160' -%} + {%- when 's' -%} + {%- assign sizes = '40, 60, 96, 150, 210, 320' -%} + {%- assign points = '(max-width: 320px) 40px, (max-width: 480px) 60px, (max-width: 768px) 96px, (max-width: 1200px) 150px, (max-width: 1680px) 210px, 320px' -%} + {%- assign width = '320' -%} + {%- when 'm' -%} + {%- assign sizes = '80, 120, 192, 300, 420, 640' -%} + {%- assign points = '(max-width: 320px) 80px, (max-width: 480px) 120px, (max-width: 768px) 192px, (max-width: 1200px) 300px, (max-width: 1680px) 420px, 640px' -%} + {%- assign width = '640' -%} + {%- when 'l' -%} + {%- assign sizes = '160, 240, 384, 600, 840, 1280' -%} + {%- assign points = '(max-width: 320px) 160px, (max-width: 480px) 240px, (max-width: 768px) 384px, (max-width: 1200px) 600px, (max-width: 1680px) 840px, 1280px' -%} + {%- assign width = '1280' -%} + {%- when 'xl' -%} + {%- assign sizes = '240, 360, 576, 900, 1260, 1920' -%} + {%- assign points = '(max-width: 320px) 240px, (max-width: 480px) 360px, (max-width: 768px) 576px, (max-width: 1200px) 900px, (max-width: 1680px) 1260px, 1920px' -%} + {%- assign width = '1920' -%} + {%- when 'xxl' -%} + {%- assign sizes = '320, 480, 768, 1200, 1680, 2560' -%} + {%- assign points = '(max-width: 320px) 320px, (max-width: 480px) 480px, (max-width: 768px) 768px, (max-width: 1200px) 1200px, (max-width: 1680px) 1680px, 2560px' -%} + {%- assign width = '2560' -%} + {%- when 'flexible' -%} + {%- assign sizes = '320, 480, 576, 900, 840, 1280' -%} + {%- assign points = '(max-width: 320px) 320px, (max-width: 480px) 480px, (max-width: 768px) 576px, (max-width: 1200px) 900px, (max-width: 1680px) 840px, 1280px' -%} + {%- assign width = '1280' -%} + {%- when 'logo' -%} + {%- assign sizes = '420, 640' -%} + {%- assign points = '(max-width: 360px) 420px, 640px' -%} + {%- assign width = '640' -%} + {%- endcase -%} + + {% comment %} override sizes and points with custom values that usually have only one width/size {% endcomment %} + {%- if custom_width != blank and custom_size != blank -%} + {%- assign sizes = custom_width -%} + {%- assign points = custom_size -%} + {%- assign width = custom_width | split: ',' | last | plus: 0 -%} + {%- endif -%} + + {% comment %} override height with custom value {% endcomment %} + {%- if custom_height != blank -%} + {%- assign height = custom_height | split: ',' | last | plus: 0 -%} + {%- endif -%} + + {%- assign inner_height = height | default: '100%' -%} + {%- assign inner_width = width -%} + + {% comment %} set image src {% endcomment %} + {%- assign paths = '' -%} + {%- assign sizes = sizes | split: ', ' -%} + + {%- for size in sizes -%} + {%- assign img_path = dynamic_url | image_url: width: size | append: ' ' | append: size | append: 'w' -%} + + {%- if forloop.first == false -%} + {%- assign paths = paths | append: ', ' -%} + {%- endif -%} + + {%- assign paths = paths | append: img_path -%} + {%- endfor -%} + + {%- assign source_map = sizes -%} + {%- assign breakpoints = points -%} + {%- endif -%} + + {% comment %} get initial image type and ensure we support it {% endcomment %} + {%- for type in featured_types -%} + {%- assign image_type = '.' | append: type -%} + + {%- if featured_type == '' and image_url contains image_type -%} + {%- assign featured_type = image_type -%} + {%- break -%} + {%- endif -%} + {%- endfor -%} + + {% comment %} convert images to the most in-demand formats {% endcomment %} + {%- if featured_type != blank and image_url contains featured_type -%} + {%- assign src_jpg = '' -%} + {%- assign src_png = '' -%} + {%- assign src_webp = '' -%} + + {%- if featured_type != '.jpg' and featured_type != '.jpeg' -%} + {%- assign jpg = 'jpg' -%} + {%- endif -%} + {%- if featured_type != '.png' -%} + {%- assign png = 'png' -%} + {%- endif -%} + {%- if featured_type != '.webp' -%} + {%- assign webp = 'webp' -%} + {%- endif -%} + + {%- for item in source_map -%} + {%- assign path_jpg = image_url | image_url: width: item, format: jpg | append: ' ' | append: item | append: 'w' -%} + {%- assign path_png = image_url | image_url: width: item, format: png | append: ' ' | append: item | append: 'w' -%} + {%- assign path_webp = image_url | image_url: width: item, format: webp | append: ' ' | append: item | append: 'w' -%} + + {%- if forloop.first == false -%} + {%- assign src_jpg = src_jpg | append: ', ' -%} + {%- assign src_png = src_png | append: ', ' -%} + {%- assign src_webp = src_webp | append: ', ' -%} + {%- endif -%} + + {%- assign src_jpg = src_jpg | append: path_jpg -%} + {%- assign src_png = src_png | append: path_png -%} + {%- assign src_webp = src_webp | append: path_webp -%} + {%- endfor -%} + {%- endif -%} + + {%- if keep_type and featured_type == '.png' or keep_type and featured_type == '.jpg' or keep_type and featured_type == '.jpeg' -%} + {%- assign keep_type = true -%} + {%- endif -%} + + {%- if image_use_focal and image_fit == 'cover' -%} + {%- assign image_focal_points = focal | remove: '[' | remove: ']' | strip | split: ',' -%} + {%- assign image_focal_x = image_focal_points[0] -%} + {%- assign image_focal_y = image_focal_points[1] -%} + + {%- if image_focal_x contains '.' or image_focal_y contains '.' -%} + {%- assign image_points = true -%} + {%- endif -%} + {%- endif -%} + + + {% comment %} load low quality image placeholder {% endcomment %} + {%- if loading_strategy == 'lazy' -%} + {%- unless image_points -%} + + {%- endunless -%} + {%- endif -%} + + {% comment %} load high quality multiformat image {% endcomment %} + + {% unless keep_type %} + {%- if featured_type == '.webp' or featured_type == '.jpg' or featured_type == '.jpeg' or featured_type == '.png' -%} + + {%- endif -%} + {% endunless %} + + {%- if featured_type == '.jpg' or featured_type == '.jpeg' -%} + + {%- endif -%} + + {%- if featured_type == '.png' -%} + + {%- endif -%} + + {{ image_alt | escape }} + + +{%- endif -%} diff --git a/src/snippets/logo.liquid b/src/snippets/logo.liquid new file mode 100644 index 0000000..365f57a --- /dev/null +++ b/src/snippets/logo.liquid @@ -0,0 +1,107 @@ +{%- comment -%} + This snippet renders the logo image, SVG or text title + + Parameters: + - logo: the logo image object + - logo_floated: the floating logo image object + - logo_title: the custom text title for the logo + - loading: 'eager' or 'lazy' (default is 'lazy') + - routes: the routes object + - section_type: the section type where the logo is included (e.g. 'header', 'footer') + - shop: the shop object + - svg: the logo SVG code + - svg_floated: the floating logo SVG code + - transparency: whether to show floating logo or SVG + + Required parameters: + - logo OR svg + - routes + - section_type + - shop + + Usage: + + {%- render 'logo', + logo: logo_image, + logo_floated: logo_image_floated, + logo_title: 'Your Title', + loading: 'eager', + routes: routes, + section_type: section.type, + shop: shop, + svg: svg_code, + svg_floated: svg_code_floating, + transparency: true + -%} +{%- endcomment -%} + +{%- assign loading_strategy = loading | default: 'lazy' -%} + + + diff --git a/src/snippets/menu.liquid b/src/snippets/menu.liquid new file mode 100644 index 0000000..1ecf76e --- /dev/null +++ b/src/snippets/menu.liquid @@ -0,0 +1,162 @@ +{%- comment -%} + + This snippet renders a menu with support for dropdowns + + Parameters: + - content: additional content to include below the menu items (default is empty) + - menu: the navigation menu object to render + - menu_back_label: label for the 'Back' button in mobile dropdowns (default is 'link.title') + - menu_id: unique ID for the menu instance (e.g. block.id) + - menu_levels: number of menu nested dropdown menus to display (max. 3 levels) + - section_type: the section type where the menu is included (e.g. 'header', 'footer') + - settings: theme settings + - socials: social links to include at the bottom of the mobile menu (default is true) + - use_opener: whether to include a menu opener on mobile, e.g. hamburger button to open menu (default is true) + + Required parameters: + - menu + - menu_id + - section_type + - settings + + Usage: + + {%- render 'menu', + content: element_menu_content, + menu: main_menu, + menu_back_label: 'Go back', + menu_id: block.id, + menu_levels: number, + section_type: section.type, + settings: settings, + socials: false, + use_opener: false + -%} +{%- endcomment -%} + +{%- assign menu = menu | default: '' -%} + +{%- if menu != blank -%} + {%- assign content = content | default: '' -%} + {%- assign menu_levels = menu_levels | default: 1 -%} + {%- assign menu_items = menu.items -%} + {%- assign socials = socials | default: true -%} + {%- assign use_opener = use_opener | default: true -%} + + {%- capture icon_arrow -%} + + {%- render 'icon-arrow' -%} + + {%- endcapture -%} + + +{%- endif -%} + diff --git a/src/snippets/search.liquid b/src/snippets/search.liquid new file mode 100644 index 0000000..351d6af --- /dev/null +++ b/src/snippets/search.liquid @@ -0,0 +1,48 @@ +{%- comment -%} + This snippet renders a search form with a toggleable input field. + + Parameters: + - closer_label: label for the 'Close search' button in the search form + - routes: the routes object + - search_placeholder: placeholder text for the search input (default is 'Search...') + - section_type: the section type where the search is included (e.g. 'header', 'footer') + + Required parameters: + - routes + - section_type + + Usage: + + {%- render 'search', + closer_label: closer_label, + placeholder: search_placeholder, + routes: routes, + section_type: section.type + -%} +{%- endcomment -%} + + diff --git a/src/snippets/socials.liquid b/src/snippets/socials.liquid new file mode 100644 index 0000000..6d5a99b --- /dev/null +++ b/src/snippets/socials.liquid @@ -0,0 +1,59 @@ +{%- comment -%} + + This snippet is used for rendering the social networks icons + + Usage: + + {%- render 'socials', settings: settings -%} + +{%- endcomment -%} + +{%- assign names = 'instagram, tiktok, facebook, x, linkedin, youtube, vimeo, pinterest, snapchat, tumblr' | split: ', ' -%} + +{%- capture links_arr -%} + {{- settings.social_instagram -}}, + {{- settings.social_tiktok -}}, + {{- settings.social_facebook -}}, + {{- settings.social_x -}}, + {{- settings.social_linkedin -}}, + {{- settings.social_youtube -}}, + {{- settings.social_vimeo -}}, + {{- settings.social_pinterest -}}, + {{- settings.social_snapchat -}}, + {{- settings.social_tumblr }} +{%- endcapture -%} + +{%- assign links = links_arr | split: ',' -%} + +{%- if links.size > 0 -%} + +{%- endif -%} + diff --git a/src/svg/_utils/optimize-svg.js b/src/svg/_utils/optimize-svg.js new file mode 100644 index 0000000..ed448cf --- /dev/null +++ b/src/svg/_utils/optimize-svg.js @@ -0,0 +1,61 @@ +const fs = require('fs') +const path = require('path') +const { optimize } = require('svgo') + +const svgoConfig = { + plugins: [ + 'removeComments', + 'removeMetadata', + 'removeEditorsNSData', + 'cleanupAttrs', + 'cleanupNumericValues', + 'convertPathData', + 'removeEmptyAttrs', + 'removeUselessStrokeAndFill', + 'cleanupListOfValues', + 'sortAttrs' + ] +} + +const optimizeSvgFiles = (srcDir, destDir) => { + if (!fs.existsSync(destDir)) { + fs.mkdirSync(destDir, { recursive: true }) + } + + const files = fs.readdirSync(srcDir) + + files.forEach(file => { + // Skip the _utils directory + if (file === '_utils') return + + if (file.endsWith('.liquid')) { + const srcPath = path.join(srcDir, file) + const destPath = path.join(destDir, file) + + let content = fs.readFileSync(srcPath, 'utf8') + + try { + // Optimize the SVG content + const result = optimize(content, svgoConfig) + const optimizedContent = result.data + + fs.writeFileSync(destPath, optimizedContent) + + const originalSize = Buffer.byteLength(content, 'utf8') + const optimizedSize = Buffer.byteLength(optimizedContent, 'utf8') + const reduction = ((originalSize - optimizedSize) / originalSize * 100).toFixed(1) + + console.log(`${file}: ${originalSize} → ${optimizedSize} bytes (${reduction}% reduction)`) + } catch (error) { + console.error(`Error optimizing ${file}:`, error.message) + // Fallback: copy original file + fs.copyFileSync(srcPath, destPath) + } + } + }) +} + +// Run the optimization +const srcDir = path.resolve(__dirname, '..') +const destDir = path.resolve(__dirname, '../../../snippets') +optimizeSvgFiles(srcDir, destDir) diff --git a/src/svg/icon-arrow.liquid b/src/svg/icon-arrow.liquid new file mode 100644 index 0000000..68145da --- /dev/null +++ b/src/svg/icon-arrow.liquid @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/svg/icon-search.liquid b/src/svg/icon-search.liquid new file mode 100644 index 0000000..2d602af --- /dev/null +++ b/src/svg/icon-search.liquid @@ -0,0 +1 @@ +