Vista previa en vídeo
GratisHeroPortafolio
Cosméticos Floriss
El prompt
ID: floriss-face-cream-hero
Category: Beauty / Skincare / Cosmetics Hero
Type: hero
Create a single-file HTML+CSS+vanilla-JS hero section with the following exact specifications:
## Tech Stack
* Deliverable filename: `floriss-hero.html`.
* Plain HTML + CSS + vanilla JavaScript. No frameworks, no build step.
* Fonts: Google Fonts `Aboreto` (display serif for the title), `Afacad` (weights 400/500, body), `Playfair Display` italic (script substitute for logo/price). Load via:
```html
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Aboreto&family=Afacad:wght@400;500&family=Playfair+Display:ital@1&display=swap" rel="stylesheet">
```
* Global resets: `*{margin:0;padding:0;box-sizing:border-box;}`. `html,body{height:100%;width:100%;font-family:'Afacad',sans-serif;overflow:hidden;}`. `img,video{display:block;}`. `a{text-decoration:none;color:inherit;}`.
## Canvas & Scaling
TRUE FULLSCREEN layout — no fixed-size design canvas, no letterboxing, no JS scale/transform of any kind. Every block is `position:fixed` and anchored directly to its nearest real viewport edge:
* `.stage{position:fixed;inset:0;width:100vw;height:100vh;overflow:hidden;background:linear-gradient(180deg,#ffffff 0%,#fff9f7 78%,#ffebdb 100%);}` — fullscreen root. The gradient is now just the fallback color showing through before/behind the background video (see Elements §1) — it is no longer the sole background of this design.
* `--edge:42px` — shared left/right header margin.
## Design Tokens
```css
:root{
--edge:42px;
--ink:#333;
--peach:#ffcbb4;
--peach-deep:#ffb99c;
}
```
## Elements
1. **Background video** — `.bg-video{position:fixed;inset:0;width:100%;height:100%;object-fit:cover;z-index:0;}` — full-bleed video sitting behind every other element, `autoplay muted loop playsinline`, `object-fit:cover` so it always fills the viewport with no letterboxing regardless of window size/aspect ratio. No entrance animation — it starts playing immediately on load. Markup:
```html
<video class="bg-video" autoplay muted loop playsinline>
<source src="https://api.promptsites.net/s3/promptsites/previews/4ac4439d-f2fb-434a-b9a7-461af6622d9f.mp4" type="video/mp4">
</video>
```
This replaces the earlier static "FLORISS" wordmark image and the central product photo, both removed from this design — the video is now the sole visual centerpiece of the hero.
2. **Header** — `.header{position:fixed;left:var(--edge);right:var(--edge);top:30px;height:40px;z-index:20;display:flex;align-items:center;justify-content:space-between;}`, fading down on load.
* Logo (left): `.logo{font-family:'Playfair Display',serif;font-style:italic;font-weight:500;font-size:32px;letter-spacing:-.64px;color:var(--ink);text-transform:capitalize;}` — text **"Floriss"**.
* `.header-right{display:flex;align-items:center;gap:80px;}` containing:
* `.nav{display:flex;align-items:center;gap:40px;}` — four links **Shop / About / Topics / Contact**, `font-size:22px;letter-spacing:.22px;color:var(--ink);`. Underline only appears on hover (`.nav a::after{content:'';position:absolute;left:0;right:100%;bottom:0;height:1.5px;background:var(--ink);transition:right .3s cubic-bezier(.4,0,.2,1);}` `.nav a:hover::after{right:0;}`) — it must NOT sit permanently under "Shop" or any link by default; every link behaves identically and only shows the underline while hovered.
* `.lang-icons{display:flex;align-items:center;gap:71px;}` containing an "EN" + chevron language switcher (chevron is a tiny `10×5` SVG that gently bobs up and down forever — `chevronBob`, `2.6s ease-in-out infinite`), two `26×26` inline SVG icons (search, bag — scale 1.15× on hover), and a `40×40` round nav avatar photo. Asset: `https://api.promptsites.net/s3/promptsites/previews/afa76167-b02b-4494-92c2-ad5521995870.png`.
3. **Left copy block** — `.hero-left{position:fixed;left:var(--edge);bottom:30px;width:329px;z-index:10;}`.
* `<h1><span>Floriss</span><span>Face Cream</span></h1>` — TWO lines, each wrapped in its own `<span>` that slides up from below (`opacity:0;transform:translateY(100%)` → `lineIn`, `.7s`, staggered `.4s`/`.55s`), with the `<h1>` itself given `overflow:hidden` so each line's slide-in is masked/clipped rather than visible sliding over content above it. `font-family:'Aboreto',serif;font-size:54px;line-height:60px;letter-spacing:-1.08px;color:var(--ink);text-transform:capitalize;`.
* `.hero-desc{margin-top:14px;font-size:16px;line-height:19px;letter-spacing:.16px;color:var(--ink);}` — text **"Lightweight daily cream with hyaluronic acid and vitamins for deep hydration and natural glow. 100 ml."**, fades up at delay `.75s`.
* `.price{margin-top:15px;font-family:'Playfair Display',serif;font-style:italic;font-size:32px;letter-spacing:.32px;color:var(--ink);}` — markup `Price $<span class="counter" id="priceValue">30</span>`, fades up at delay `.9s`. The number lives in a `.counter` span (see **Interactive Behavior** — it ticks up over time, shared with the review count below).
* `.add-cart` button, fades up at delay `1.05s` (see **Button Component** below for shared styling).
4. **Review carousel** (right side) — `.review-wrap{position:fixed;right:35px;bottom:30px;width:241px;z-index:10;}`, fades up at delay `.5s`.
* `.review-card{position:relative;width:241px;height:229px;background:#ffefe8;overflow:hidden;}` containing two `.review-slide` panels (5-star rating block, review text, reviewer photo+name) that swap via a vertical "conveyor" motion rather than a flat crossfade — see **Interactive Behavior**.
* `.review-slide{position:absolute;left:24px;top:24px;right:24px;display:flex;flex-direction:column;gap:18px;opacity:0;transform:translateY(56px) scale(.94);transition:opacity .85s cubic-bezier(.22,1,.36,1),transform .85s cubic-bezier(.22,1,.36,1);pointer-events:none;z-index:1;}`. `.active{opacity:1;transform:translateY(0) scale(1);pointer-events:auto;z-index:2;}`. `.exit{opacity:0;transform:translateY(-56px) scale(.94);z-index:1;}`.
* `.stars{display:flex;gap:1px;}` — 5 star SVGs (`19×19`, filled `#e8a374`) each with a subtle infinite twinkle (`starTwinkle`: opacity/scale pulse, `2.4s ease-in-out infinite`), staggered `.15s` apart per star so the sparkle ripples left-to-right rather than all 5 pulsing in unison.
* Slide 1: rating `5.0`, text "Love this cream! So light yet super hydrating — my skin feels fresh and glowing.", reviewer "Chloe Bennett" with photo `https://api.promptsites.net/s3/promptsites/previews/86694fa3-2667-491d-a949-dd74198edf01.png`.
* Slide 2: rating `4.9` (5th star rendered as an outline/partial star, `stroke` only), text "Finally found a cream that hydrates without feeling heavy. Absolutely love it!", reviewer "Chloe Bennett" with photo `https://api.promptsites.net/s3/promptsites/previews/151727d9-d5a2-42b8-b124-b44288c71a8a.png`.
* `.view-reviews` button below the card — markup `View all <span class="counter" id="reviewCount">582</span> reviews` (count ticks up — see **Interactive Behavior**). See **Button Component** below for shared styling; on hover it goes to `var(--peach-deep)` for clear contrast against its own white default background (an earlier near-white hover color blended into the surrounding page and was rejected for being too subtle).
## Button Component (`.btn`, shared by Add to cart / View all reviews)
```css
.btn{position:relative;overflow:hidden;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:transform .25s ease,box-shadow .25s ease,background-color .25s ease;}
.btn::after{content:'';position:absolute;top:0;left:-60%;width:35%;height:100%;background:linear-gradient(115deg,transparent,rgba(255,255,255,.75),transparent);transform:skewX(-18deg);transition:left .65s cubic-bezier(.4,0,.2,1);}
.btn:hover::after{left:130%;}
.btn:hover{transform:translateY(-3px);}
.btn:active{transform:translateY(-1px);}
```
A diagonal highlight sweeps across the button on hover (via `::after`), plus a small lift. Buttons have **no drop shadow** — `.btn:hover` intentionally omits `box-shadow`; only the lift + shine communicate the hover state.
`.add-cart{margin-top:20px;width:296px;height:68px;background:var(--peach);font-size:26px;letter-spacing:.26px;color:var(--ink);}`, `hover{background:var(--peach-deep);}`, PLUS its own independent idle shine loop on `::before` (separate pseudo-element from the shared hover `::after`, so the two never conflict): `.add-cart::before{content:'';position:absolute;top:0;left:-70%;width:40%;height:100%;background:linear-gradient(115deg,transparent,rgba(255,255,255,.6),transparent);transform:skewX(-18deg);animation:idleShine 4s ease-in-out 2.6s infinite;}` — a soft highlight quietly sweeps across the button on its own timer even when nobody is hovering it.
`.view-reviews{width:241px;height:68px;background:#fff;font-size:20px;letter-spacing:.2px;color:var(--ink);}`.
## Interactive (JS) Behavior
**Review carousel (vertical conveyor, not a crossfade)** — the outgoing card slides up and out while the next one slides up from below into place, which reads as much more visible motion than a plain opacity crossfade:
```js
(function(){
var slides = document.querySelectorAll('.review-slide');
if(slides.length < 2) return;
var idx = 0;
setInterval(function(){
var current = slides[idx];
var next = (idx + 1) % slides.length;
current.classList.remove('active');
current.classList.add('exit');
slides[next].classList.add('active');
setTimeout(function(){ current.classList.remove('exit'); }, 900);
idx = next;
}, 4500);
})();
```
Every `4500ms`.
**Live counters** — both the price and the review count quietly tick upward on their own independent timers forever, each with a small "bump" scale-pop on every increment, like a live storefront:
```css
.counter{display:inline-block;}
.counter.bump{animation:countBump .35s ease;}
@keyframes countBump{0%{transform:scale(1)}35%{transform:scale(1.3)}100%{transform:scale(1)}}
```
```js
function startCounter(id, minDelay, maxDelay){
var el = document.getElementById(id);
if(!el) return;
var value = parseInt(el.textContent, 10) || 0;
function bump(){
value += 1;
el.textContent = value;
el.classList.remove('bump');
void el.offsetWidth; // force reflow so the bump animation can re-trigger back-to-back
el.classList.add('bump');
setTimeout(bump, minDelay + Math.random() * (maxDelay - minDelay));
}
setTimeout(bump, minDelay + Math.random() * (maxDelay - minDelay));
}
startCounter('reviewCount', 4000, 9000); // review count: +1 every 4–9s, randomized
startCounter('priceValue', 1000, 1000); // price: +1 exactly every 1s
```
The review count increments on a randomized 4–9 second cadence (feels organic, like real reviews trickling in); the price increments by exactly $1 every second (a much faster, very obviously "live" ticking effect, distinct in pace from the review counter).
## Animations
Keyframes (exact):
```css
@keyframes downIn{from{opacity:0;transform:translateY(-16px)}to{opacity:1;transform:translateY(0)}}
@keyframes upIn{from{opacity:0;transform:translateY(24px)}to{opacity:1;transform:translateY(0)}}
@keyframes lineIn{to{opacity:1;transform:translateY(0)}}
@keyframes idleShine{0%,64%{left:-70%}82%{left:130%}100%{left:130%}}
@keyframes starTwinkle{0%,100%{opacity:1;transform:scale(1)}50%{opacity:.55;transform:scale(.85)}}
@keyframes chevronBob{0%,100%{transform:translateY(0)}50%{transform:translateY(2px)}}
@keyframes countBump{0%{transform:scale(1)}35%{transform:scale(1.3)}100%{transform:scale(1)}}
```
Entrance schedule, in load order:
* `.bg-video` → no entrance animation, starts playing immediately on load (muted, looped).
* `.header` → `downIn` → `.6s` → delay `0s`.
* `.review-wrap` → `upIn` → `.8s` → delay `.5s`.
* `.hero-left h1 span:nth-child(1)` → `lineIn` → `.7s` → delay `.4s`.
* `.hero-left h1 span:nth-child(2)` → `lineIn` → `.7s` → delay `.55s`.
* `.hero-desc` → `upIn` → `.7s` → delay `.75s`.
* `.price` → `upIn` → `.7s` → delay `.9s`.
* `.add-cart` → `upIn` → `.7s` → delay `1.05s` → its idle `::before` shine starts at delay `2.6s`, `4s` cycle, forever.
* `.lang svg` (chevron) → `chevronBob` → `2.6s` infinite, from load.
* `.stars svg` → `starTwinkle` → `2.4s` infinite, staggered `.15s` per star, from load.
* Review carousel → advances every `4.5s`, forever, from load.
* Review count / price counters → tick on their own independent randomized/fixed timers, forever, from load.
`prefers-reduced-motion: reduce` behavior:
```css
@media (prefers-reduced-motion: reduce){
*{animation-duration:.01ms !important;animation-iteration-count:1 !important;transition-duration:.01ms !important;}
}
```
## Z-index Layering
* `.bg-video` → 0
* `.hero-left` / `.review-wrap` → 10
* `.header` → 20
## Notes
* Do not give either `.btn` or its hover state a `box-shadow` — buttons on this design are intentionally flat, communicating hover only through the lift + shine sweep.
* The nav underline must only ever appear on `:hover` — do not special-case "Shop" (or any link) to show it by default via an `.active` class; all four links share identical idle styling.
* The old "FLORISS" background wordmark and the central product photo (with its transform-collision-avoidant `.product-pos` / `.product-shadow` / `.product-float` structure) have been removed entirely from this design — the background video is now the hero's sole visual centerpiece, with the header, left copy block, and review carousel floating directly over it.
Please ensure the output is a clean, production-ready single HTML file that renders pixel-identically to this spec, filling the entire browser viewport edge-to-edge at any window size with no letterboxing and no canvas scaling — every block must remain correctly pinned to its respective viewport edge via `position:fixed` and the shared `--edge:42px` margin.