Artifacts Gallery Guilds Search Wiki Login Register

Welcome, Guest. Please login or register. - Thinking of joining?
a Summer day - @668.75
Activity rating: Four Stars Posts & Arts: 82/1k.beats Random | Recent Posts | Guild Recents
News: :4u: ~~~~~~~~~~~  :4u: Guild Events: There are no events!

+  MelonLand Forum
|-+  Materials & Info
| |-+  ♺ ∙ Web Crafting Materials
| | |-+  Tutorial: CSS-based 88x31 rotation


« previous next »
Pages: [1] Print Embed
Author Topic: Tutorial: CSS-based 88x31 rotation  (Read 93 times)
Dan Q
Hero Member ⚓︎
*****
View Profile WWWArt


I have no idea what I am doing
⛺︎ My Room
RSS: RSS

Guild Memberships:
Artifacts:
Dan Q Cruisin'I DIDN'T meet Dan Q on Melonland!Visited on Melon's 10th Anniversary!
« on: a Summer day » Embed

Inspired by a conversation in the chat involving @larvapuppy, @John, and @Noah_S, I decided to have a go at making something that would be:

- 88x31 pixels in size
- rotate through a number of different 88x31 buttons, one at a time
- in pure HTML+CSS (no JS)

Here's what I came up with:

Part 1: HTML

My buttons are all <img>s; each is wrapped in a <a href="..."> pointing to the owners' site. Semantically, that seems like it's a list, so I'll wrap the whole thing in <li>s and a <ul>, giving me this:


Code
<div class="carousel-88x31" style="--button-count: 45; --button-duration: 1.1;">
  <ul>
    <li>
      <a class="blogroll-has-img" href="https://blazingcobaltx.neocities.org/">
        <img alt="A Realm Beyond" height="31" loading="lazy" src="https://bcdn.danq.me/_q23u/blogroll/blazingcobaltx.neocities.org.gif" width="88">
      </a>
    </li>
    <li>
      <a class="blogroll-has-img" href="https://zhongvie.neocities.org/">
        <img alt="aid's small corner" height="31" loading="lazy" src="https://bcdn.danq.me/_q23u/blogroll/zhongvie.neocities.org.png" width="88">
      </a>
    </li>

    <!-- lots more <li>s! -->

  </ul>
</div>

Note my addition of two CSS variables, which I'll use in CSS later. These make it easy for me to change my HTML in future, and mean that it's possible to have multiple of these carousels on a single page (each with their own variables):

- --button-count: 45; - how many buttons are in my carousel: CSS needs this to make the animation "step" through each of them as well as to calculate how far it needs to animate
- --button-duration: 1.1; - how long should each button be shown for, in seconds: CSS needs this to tune the speed of the animation

Let's write the CSS. As usual, I've littered it with comments to explain my thinking at each step:

Part 2: CSS


Code
.carousel-88x31 {
  /* I often use box-sizing: border-box; on my entire page because it makes more sense to me.
   * But here, we absolutely want the box to be sized to fit its CONTENTS, which have a known
   * and fixed size. So I ensure I'm on `box-sizing: content-box;`. This means that if I add
   * a border (and I have, later!) then it doesn't eat away at the edges of the 88x31s!
   */
  box-sizing: content-box;
  /* The size of my component and its border: */
  width: 88px;
  height: 31px;
  border: 2px solid black;
  /* hide the buttons that aren't in view right now: comment this out while debugging! */
  overflow: hidden;

  ul {
    /* The <ul> needs to stop looking "like a list"; this is the laziest way to do that: */
    all: unset;
    /* Lay out all of my buttons in a column, packed close together (no gap: ...;); - */
    display: flex;
    flex-direction: column;
    /* Use an animation called "button-stepper" (which we'll define below);
     * - infinite -loop it forever
     * - calc(var(--button-count) * var(--button-duration) * 1s) - show each button for 1
     *   second, multiplied by the '--button-duration' we defined in the HTML; we need to
     *   multiply THAT by the '--button-count' because this is the duration of the WHOLE
     *   ANIMATION (i.e. the cycle of ALL the buttons).
     * - steps(var(--button-count) , jump-end) - how many "steps" are there: one per
     *   button, of course! This makes the carousel "jump" between each button instead of
     *   smoothly scrolling, which feels like it makes more sense for 88x31s. The 'jump-end'
     *   ensures that each button is shown before we animate to the next; without it the
     *   final button never appears!
     */
    animation: button-stepper
               infinite
               calc(var(--button-count) * var(--button-duration) * 1s)
               steps(var(--button-count) , jump-end);

    /* If the <ul> or anything in it is hovered or focussed, pause the animation (so people
     * can click on the buttons!
     */
    &:hover, &:has(:hover), &:focus, &:has(:focus) {
      animation-play-state: paused;
    }
  }

  /* Stop the <li>s looking like they're a list (e.g. having bullets!) too: */
  li {
    all: unset;
  }

  /* Force the size of the links so they don't try to render the "white space"
   * around the <img>s.
   */
  a {
    display: block;
    width: 88px;
    height: 31px;
  }
}

/* Here's the animation. It's pretty simple. We just move in the Y-dimension
 * (vertically) from Y=0 to Y=-100, i.e. we move the contents "up" until they
 * reach the end.
 */
@keyframes button-stepper {
  0% {     transform: translateY(0); }
100% { transform: translateY(-100%); }
}

That's all there is to it! I've put a working example onto an Everyone Page if you want to see.

(In the unlikely event I've done anything innovative or transformative above, please consider all this code your choice of CC0/Public Domain/Unlicensed.)

Logged

https://danq.me/_q26t/badges/dan-q-88x31-lighter.gif https://danq.me/_q26t/badges/dan-q-88x31-peekaboo-scroller.gif https://beige-buttons.danq.dev/beige-buttons-88x31.gif https://embed-html.danq.dev/embed-html-88x31.gif

Artifact Swap: PolyamorousI met Dan Q on Melonland!Joined 2025!
schlime9k
Casual Poster ⚓︎
*
View Profile WWW


Forever personalising my profile
⛺︎ My Room
StatusCafe: schlime9k
iMood: schlime9k
RSS: RSS

Guild Memberships:
Artifacts:
Visited on Melon's 10th Anniversary!Joined 2025!
« Reply #1 on: a Summer day » Embed

Very awesome yet again Dan! This is very interesting because you can actually use this code and fit it for different images you want to flick through, like those fake banner ads I love seeing on other people's websites (that I've been meaning to add onto my landing page but alas, procrastination and laziness), or something similar to the 400x40 MelonLinks thingy, pretty sure that changes every 20-something seconds or so (I also need to make my own image and add that on there, but alas, I am schlime, the laziest person on the planet).

I will def add this onto my website, but probably not for 88x31 buttons, just because for me I like seeing all the buttons on the webpage at once, or sometimes people have a marquee with all the buttons, I love that! I will probably try javascript first before seeing if I can alter your code up there to fit my fake banner ad idea. If I do I'll reply on here with how I did that :wizard:

Logged

https://schlime.online/images/buttons/wsw.gif

Artifact Swap: Snail MailSMK Luigismuggler?? i hardly knower!Ace Honey
Dan Q
Hero Member ⚓︎
*****
View Profile WWWArt


I have no idea what I am doing
⛺︎ My Room
RSS: RSS

Guild Memberships:
Artifacts:
Dan Q Cruisin'I DIDN'T meet Dan Q on Melonland!Visited on Melon's 10th Anniversary!
« Reply #2 on: a Summer day » Embed

@schlime9k: 100% this would work for "banner ads" or whatever else too. Here's a more-generic version of the CSS that puts the 88px and 31px sizing into variables at the top (which can then be overridden in your HTML or CSS!):

Code
.carousel-88x31 {
  /* Modify this here, or override it in the style="..." of your <div>! */
  --width: 88px;
  --height: 31px;

  /* I often use box-sizing: border-box; on my entire page because it makes more sense to me.
   * But here, we absolutely want the box to be sized to fit its CONTENTS, which have a known
   * and fixed size. So I ensure I'm on `box-sizing: content-box;`. This means that if I add
   * a border (and I have, later!) then it doesn't eat away at the edges of the 88x31s!
   */
  box-sizing: content-box;
  /* The size of my component and its border: */
  width: var(--width);
  height: var(--height);
  border: 2px solid black;
  /* hide the buttons that aren't in view right now: comment this out while debugging! */
  overflow: hidden;
 
  ul {
    /* The <ul> needs to stop looking "like a list"; this is the laziest way to do that: */
    all: unset;
    /* Lay out all of my buttons in a column, packed close together (no gap: ...;); - */
    display: flex;
    flex-direction: column;
    /* Use an animation called "button-stepper" (which we'll define below);
     * - infinite -loop it forever
     * - calc(var(--button-count) * var(--button-duration) * 1s) - show each button for 1
     *   second, multiplied by the '--button-duration' we defined in the HTML; we need to
     *   multiply THAT by the '--button-count' because this is the duration of the WHOLE
     *   ANIMATION (i.e. the cycle of ALL the buttons).
     * - steps(var(--button-count) , jump-end) - how many "steps" are there: one per
     *   button, of course! This makes the carousel "jump" between each button instead of
     *   smoothly scrolling, which feels like it makes more sense for 88x31s. The 'jump-end'
     *   ensures that each button is shown before we animate to the next; without it the
     *   final button never appears!
     */
    animation: button-stepper
               infinite
               calc(var(--button-count) * var(--button-duration) * 1s)
               steps(var(--button-count) , jump-end);
 
    /* If the <ul> or anything in it is hovered or focussed, pause the animation (so people
     * can click on the buttons!
     */
    &:hover, &:has(:hover), &:focus, &:has(:focus) {
      animation-play-state: paused;
    }
  }
 
  /* Stop the <li>s looking like they're a list (e.g. having bullets!) too: */
  li {
    all: unset;
  }
 
  /* Force the size of the links so they don't try to render the "white space"
   * around the <img>s.
   */
  a {
    display: block;
    width: var(--width);
    height: var(--height);
  }
}
 
/* Here's the animation. It's pretty simple. We just move in the Y-dimension
 * (vertically) from Y=0 to Y=-100, i.e. we move the contents "up" until they
 * reach the end.
 */
@keyframes button-stepper {
  0% {     transform: translateY(0); }
100% { transform: translateY(-100%); }
}

Logged

https://danq.me/_q26t/badges/dan-q-88x31-lighter.gif https://danq.me/_q26t/badges/dan-q-88x31-peekaboo-scroller.gif https://beige-buttons.danq.dev/beige-buttons-88x31.gif https://embed-html.danq.dev/embed-html-88x31.gif

Artifact Swap: PolyamorousI met Dan Q on Melonland!Joined 2025!
schlime9k
Casual Poster ⚓︎
*
View Profile WWW


Forever personalising my profile
⛺︎ My Room
StatusCafe: schlime9k
iMood: schlime9k
RSS: RSS

Guild Memberships:
Artifacts:
Visited on Melon's 10th Anniversary!Joined 2025!
« Reply #3 on: a Summer day » Embed

Here's a more-generic version of the CSS that puts the 88px and 31px sizing into variables at the top (which can then be overridden in your HTML or CSS!)
Dan I love you


Logged

https://schlime.online/images/buttons/wsw.gif

Artifact Swap: Snail MailSMK Luigismuggler?? i hardly knower!Ace Honey
Pages: [1] Print Embed 
« previous next »
 

Melonking.Net © Always and ever was! SMF 2.0.19 | SMF © 2021 | Privacy Notice | Send Feedback | Supporters ♥ Forum Guide | Rules | RSS | WAP | Mobile


MelonLand Badges and Other Melon Sites!

MelonLand Project! Visit the MelonLand Forum! Support the Forum
Visit Melonking.Net! Visit the Gif Gallery! Pixel Sea TamaNOTchi
@000 MelonLand Zap! Want to Login or Join ? Forum Art Hub Chat Webring Editor Recent Edits Tenement Arcade Web Guides Graphics Catalogue Wiki Newsletters Image Stream
Melon's Sites TamaNotchi Textures PixelSea GifyPet MoMG Ozwomp Online Loom Videos Leaky Webring Melonking
Tools Melon Software ArtHub Embed Maker ML Passports
Outlinks Webrings Internet Phone Book HTML Energy Declarations Hackers & Designers Frutiger Aero Forum m15o's Web Services 32Bit Cafe iMood
Minecraft: Online
Join: craft.melonking.net