diff --git a/css/styles.css b/css/styles.css index 39e71626..47ec5524 100644 --- a/css/styles.css +++ b/css/styles.css @@ -1 +1 @@ -*,:after,:before{box-sizing:border-box}::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar:hover{width:18px;background:#0004}::-webkit-scrollbar-track:hover{background:#0001}::-webkit-scrollbar-track{background:#263038}::-webkit-scrollbar-thumb{background:#ff3d00}::-webkit-scrollbar-thumb:hover{background:#fff}body{margin:0;background:#263038;font-family:Arial,Helvetica,sans-serif}body>img{display:none}body.pop{overflow:hidden}header{background:#0d161b;padding:10px 20px;min-height:50px;width:100%;display:flex;align-items:center;justify-content:space-between;position:sticky;top:0;z-index:10}header nav{display:flex;align-items:center;justify-content:center}header nav .nav-btn{display:inline-block;width:32px;height:32px;background-color:#fff;background-repeat:no-repeat;background-size:110% auto;background-position:center;border-radius:50%}header nav .nav-btn.git{background-image:url(../images/git.png)}header nav .nav-btn.codepen{background-image:url(../images/codepen.png)}header nav .nav-btn+.nav-btn{margin-left:10px}.brand{color:#fff;font-size:32px;display:inline-block;position:relative}.brand::after{content:'';position:absolute;left:20px;bottom:7px;border:3px solid #fff;border-bottom-color:#ff3d00;width:20px;height:20px;border-radius:50%;animation:brandRotation .6s linear infinite}@keyframes brandRotation{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}#main{min-height:100vh;width:100%;display:flex;align-items:center;flex-wrap:wrap;justify-content:space-between}#main .section{min-width:200px;width:33.33%;height:360px;padding:10px;position:relative;display:flex;align-items:center;justify-content:center;color:#ccc;cursor:pointer;transition:.2s linear}#main .section:nth-child(2n+1){background:rgba(0,0,0,.1)}#main .section:hover{background:rgba(0,0,0,.3)}@media (max-width:768px){#main .section{width:50%}}@media (max-width:480px){#main .section{width:100%}}footer{background:#0d161b;padding:15px;text-align:center;color:#ccc;width:100%;font-size:12px}footer a{font-size:16px;color:#fff;transition:.2s ease;text-decoration:none;display:inline-block;position:relative}footer a:active,footer a:hover{color:#fff;transform:scale(1.15)}.overlay{position:fixed;left:0;top:0;width:100%;height:100%;background:rgba(0,0,0,.7);z-index:2000;visibility:hidden;opacity:0;overflow-y:auto}.overlay.in{visibility:visible;opacity:1}.btn-close{position:absolute;top:0;right:0;z-index:5;line-height:20px;height:20px;width:20px;font-size:26px;font-weight:400;padding:0;background:#eee;border:none;outline:0;cursor:pointer}.popup{position:relative;transform:translateY(-20px);background:#fff;padding:20px 15px;max-width:600px;min-height:400px;margin:20px auto;width:100%;transition:.2s ease-in}.in .popup{transform:translateY(10px)}.showcase{background:#263038;margin-bottom:15px}.showcase .section{width:100%;height:300px;padding:10px;position:relative;display:flex;align-items:center;justify-content:center}.code-area .code-header{padding:5px 10px;background:#222;color:#fff;font-size:14px;position:relative}.code-area+.code-area{margin-top:10px}.copy{outline:0;border:none;background:#000;position:absolute;right:5px;top:50%;transform:translateY(-50%);color:#fff;padding:3px 8px;cursor:pointer;user-select:none}.copy::before{content:'';display:inline-block;width:10px;height:12px;border:1px solid #fff;box-shadow:2px -2px #000,3px -3px;margin-right:8px;position:relative;top:3px}code{background:#000;padding:5px 10px;display:block;white-space:pre;color:#03a9f4;min-height:30px;font-size:14px;line-height:18px}#markup{color:#f4a003}#css{max-height:200px;overflow:auto}div[data-id=prog-crak-erh]{justify-content:flex-start!important} \ No newline at end of file +*,*:after,*:before{box-sizing:border-box}::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar:hover{width:18px;background:rgba(0,0,0,.2666666667)}::-webkit-scrollbar-track:hover{background:rgba(0,0,0,.0666666667)}::-webkit-scrollbar-track{background:#263038}::-webkit-scrollbar-thumb{background:#ff3d00}::-webkit-scrollbar-thumb:hover{background:#fff}body{margin:0;background:#263038;font-family:Arial,Helvetica,sans-serif}body>img{display:none}body.pop{overflow:hidden}header{background:#0d161b;padding:10px 20px;min-height:50px;width:100%;display:flex;align-items:center;justify-content:space-between;position:sticky;top:0px;z-index:10}header nav{display:flex;align-items:center;justify-content:center}header nav .nav-btn{display:inline-block;width:32px;height:32px;background-color:#fff;background-repeat:no-repeat;background-size:110% auto;background-position:center;border-radius:50%}header nav .nav-btn.git{background-image:url("../images/git.png")}header nav .nav-btn.codepen{background-image:url("../images/codepen.png")}header nav .nav-btn+.nav-btn{margin-left:10px}.brand{color:#fff;font-size:32px;display:inline-block;position:relative}.brand::after{content:"";position:absolute;left:20px;bottom:7px;border:3px solid #fff;border-bottom-color:#ff3d00;width:20px;height:20px;border-radius:50%;animation:brandRotation .6s linear infinite}@keyframes brandRotation{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}#main{min-height:100vh;width:100%;display:flex;align-items:center;flex-wrap:wrap;justify-content:space-between}#main .section{min-width:200px;width:33.33%;height:360px;padding:10px;position:relative;display:flex;align-items:center;justify-content:center;color:#ccc;cursor:pointer;transition:.2s linear}#main .section:nth-child(2n+1){background:rgba(0,0,0,.1)}#main .section:hover{background:rgba(0,0,0,.3)}@media(max-width: 768px){#main .section{width:50%}}@media(max-width: 480px){#main .section{width:100%}}footer{background:#0d161b;padding:15px;text-align:center;color:#ccc;width:100%;font-size:12px}footer a{font-size:16px;color:#fff;transition:.2s ease;text-decoration:none;display:inline-block;position:relative}footer a:hover,footer a:active{color:#fff;transform:scale(1.15)}.overlay{position:fixed;left:0;top:0;width:100%;height:100%;background:rgba(0,0,0,.7);z-index:2000;visibility:hidden;opacity:0;overflow-y:auto}.overlay.in{visibility:visible;opacity:1}.btn-close{position:absolute;top:0px;right:0px;z-index:5;line-height:20px;height:20px;width:20px;font-size:26px;font-weight:400;padding:0;background:#eee;border:none;outline:none;cursor:pointer}.popup{position:relative;transform:translateY(-20px);background:#fff;padding:20px 15px;max-width:600px;min-height:400px;margin:20px auto;width:100%;transition:.2s ease-in}.in .popup{transform:translateY(10px)}.showcase{background:#263038;margin-bottom:15px}.showcase .section{width:100%;height:300px;padding:10px;position:relative;display:flex;align-items:center;justify-content:center}.code-area .code-header{padding:5px 10px;background:#222;color:#fff;font-size:14px;position:relative}.code-area+.code-area{margin-top:10px}.copy{outline:none;border:none;background:#000;position:absolute;right:5px;top:50%;transform:translateY(-50%);color:#fff;padding:3px 8px;cursor:pointer;user-select:none}.copy::before{content:"";display:inline-block;width:10px;height:12px;border:1px solid #fff;box-shadow:2px -2px #000,3px -3px;margin-right:8px;position:relative;top:3px}code{background:#000;padding:5px 10px;display:block;white-space:pre;color:#03a9f4;min-height:30px;font-size:14px;line-height:18px}#markup{color:#f4a003}#css{max-height:200px;overflow:auto}div[data-id=prog-crak-erh]{justify-content:flex-start !important}.fav-btn{position:absolute;top:8px;right:8px;background:none;border:none;cursor:pointer;font-size:20px;line-height:1;color:hsla(0,0%,100%,.25);z-index:2;padding:4px;transition:color .15s,transform .15s;pointer-events:auto}.fav-btn:hover{color:#ff3d00;transform:scale(1.2)}.fav-btn.active{color:#ff3d00}#main.favs-active{align-items:start;justify-content:start}#main.favs-active .section:nth-child(2n+1){background:rgba(0,0,0,0)}#main.favs-active .section:nth-child(2n+1):hover{background:rgba(0,0,0,.3)}#main.favs-active .section.alt-bg{background:rgba(0,0,0,.1)}#main.favs-active .section.alt-bg:hover{background:rgba(0,0,0,.3)}#main .section.hidden{display:none}.fav-filter-btn{display:inline-flex;align-items:center;gap:6px;padding:5px 14px;background:rgba(0,0,0,0);border:1px solid hsla(0,0%,100%,.3);border-radius:20px;color:hsla(0,0%,100%,.6);cursor:pointer;font-size:13px;font-family:Arial,Helvetica,sans-serif;transition:all .2s;margin-right:10px}.fav-filter-btn:hover{border-color:#ff3d00;color:#ff3d00}.fav-filter-btn.active{background:#ff3d00;border-color:#ff3d00;color:#fff}/*# sourceMappingURL=styles.css.map */ diff --git a/js/app.js b/js/app.js index 0641c28e..e6ba5076 100644 --- a/js/app.js +++ b/js/app.js @@ -12,6 +12,22 @@ import { SKELETON } from './loaders/skeleton.js'; const LOADERS = [...CIRCLE, ...BUBBLE, ...RECT, ...LINE, ...PROGRESS, ...TEXT, ...OBJECTS, ...GRAPH , ...SKELETON ]; +const STORAGE_KEY = 'css-loader-favourites'; + +function getFavourites() { + try { + return new Set(JSON.parse(localStorage.getItem(STORAGE_KEY)) || []); + } catch { + return new Set(); + } +} + +function saveFavourites(favs) { + localStorage.setItem(STORAGE_KEY, JSON.stringify([...favs])); +} + +let favourites = getFavourites(); +let showFavsOnly = false; const main = document.getElementById('main'); @@ -45,21 +61,43 @@ function createLoader(i){ loaderStyles.innerHTML = loader.css; shadowRoot.appendChild(loaderStyles); + // Slot lets light DOM children (fav button) render through the shadow root + shadowRoot.appendChild(document.createElement('slot')); + return sectionEl } - - +// Add favourite buttons and click handlers to each section document.querySelectorAll('#main .section').forEach(elm => { + const id = elm.dataset.id; + + // Favourite button + const favBtn = document.createElement('button'); + favBtn.className = 'fav-btn' + (favourites.has(id) ? ' active' : ''); + favBtn.innerHTML = '♥'; + favBtn.title = 'Add to favourites'; + favBtn.addEventListener('click', (e) => { + e.stopPropagation(); + if (favourites.has(id)) { + favourites.delete(id); + favBtn.classList.remove('active'); + } else { + favourites.add(id); + favBtn.classList.add('active'); + } + saveFavourites(favourites); + if (showFavsOnly) applyFilter(); + }); + elm.appendChild(favBtn); + + // Open popup on section click elm.addEventListener('click', (e) => { - let index = parseInt(e.target.dataset.index); + let index = parseInt(elm.dataset.index); let showCase = document.querySelector('.showcase'); showCase.replaceChildren(createLoader((index - 1))); - - // console.log(e); showCase.dataset.index = index; // load code @@ -73,6 +111,33 @@ document.querySelectorAll('#main .section').forEach(elm => { }) +// Favourites filter toggle button in header +const filterBtn = document.createElement('button'); +filterBtn.className = 'fav-filter-btn'; +filterBtn.innerHTML = '♥ Favourites'; +filterBtn.addEventListener('click', () => { + showFavsOnly = !showFavsOnly; + filterBtn.classList.toggle('active', showFavsOnly); + applyFilter(); +}); +document.querySelector('header nav').prepend(filterBtn); + +function applyFilter() { + main.classList.toggle('favs-active', showFavsOnly); + const sections = document.querySelectorAll('#main .section'); + let visibleIndex = 0; + sections.forEach(section => { + const isHidden = showFavsOnly && !favourites.has(section.dataset.id); + section.classList.toggle('hidden', isHidden); + section.classList.remove('alt-bg'); + if (!isHidden) { + section.classList.toggle('alt-bg', visibleIndex % 2 === 0); + visibleIndex++; + } + }); +} + + // close popup document.querySelector('.btn-close').addEventListener('click', (e) => { document.querySelector('body').classList.remove('pop') diff --git a/scss/styles.scss b/scss/styles.scss index 702e900e..07bf9f0a 100644 --- a/scss/styles.scss +++ b/scss/styles.scss @@ -299,3 +299,72 @@ div[data-id="prog-crak-erh"]{ justify-content: flex-start !important; } +.fav-btn { + position: absolute; + top: 8px; + right: 8px; + background: none; + border: none; + cursor: pointer; + font-size: 20px; + line-height: 1; + color: rgba(#fff, 0.25); + z-index: 2; + padding: 4px; + transition: color 0.15s, transform 0.15s; + pointer-events: auto; + &:hover { + color: $brand; + transform: scale(1.2); + } + &.active { + color: $brand; + } +} + +#main.favs-active { + align-items: start; + justify-content: start; + .section:nth-child(2n+1) { + background: transparent; + &:hover { + background: rgba(#000, 0.3); + } + } + .section.alt-bg { + background: rgba(#000, 0.1); + &:hover { + background: rgba(#000, 0.3); + } + } +} + +#main .section.hidden { + display: none; +} + +.fav-filter-btn { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 5px 14px; + background: transparent; + border: 1px solid rgba(#fff, 0.3); + border-radius: 20px; + color: rgba(#fff, 0.6); + cursor: pointer; + font-size: 13px; + font-family: Arial, Helvetica, sans-serif; + transition: all 0.2s; + margin-right: 10px; + &:hover { + border-color: $brand; + color: $brand; + } + &.active { + background: $brand; + border-color: $brand; + color: #fff; + } +} +