tyleo 4 days ago

Author here! Happy to answer any questions.

5
dudus 4 days ago

Very nice effect. I love these more creative realistic elements.

This one reminds me of this cool card effect

https://poke-holo.simey.me/

tyleo 4 days ago

Funny enough I also implemented the 3D cards in Forza Motorsport 7 :p

emmanueloga_ 4 days ago

Clicking around found this breakdown [1]. Expertly crafted but also really cool like the optical illusion background does a lot of the heavy lifting!

---

1: https://www.joshdance.com/100/day50/

mkbosmans 4 days ago

I noticed the blur only "sees" the underlying pixels directly below the glass surface. Any pixels outside that box, but within the blur radius do not get used in the gaussian filter. Probably the edge pixels are just repeated. You can see this when the light of the moon pops in to view when the edge of the rectangle starts to touch the moon. It would look more real if the light pixels from the moon start to through even when the box itself is still just over the dark area.

Would this be possible to achieve in CSS? I presume having a larger box with the blur, but clipping it to a smaller box or something like that.

tyleo 3 days ago

This was discussed elsewhere in the comments:

> This can be solved with CSS. Extend the background blur all the way through the element and then use CSS masks to cut out the actual shape you want.

> With this, you can remove the border (or inset box shadow), and the edge of the glass will look much, much more real

I tried this and it works! One unfortunate impact is a loss in simplicity. In the example on the page you can apply the non-JavaScript version to pretty much any element and get a nice glass effect with `border-radius` and such still functioning as expected.

Using `clip-path` I'm able to consider background pixels more correctly but it looks like I'd need an extra div and/or some sizing tricks to get everything working exactly as expected.

I'll keep noodling on this and may add an update to the page if a simple solution comes to mind.

RugnirViking 1 day ago

even if you can't figure out a simple solution I'd love to read a part 2 or an addendum with some of these other tricks.

tyleo 4 days ago

Other folks made similar comments. I’ll have to see if this is possible. Your recommendation at the bottom sounds plausible so I’ll give it a go.

gloosx 4 days ago

Do you like that subsurface scattering is really visible with a darker background? I can clearly see the circles of varying opacity in a step-like gradient, and I can clearly see the light rays static image. Is it something with my browser or how it renders the thing? I really like how it looked more before the "More subsurface scattering" came in.

tyleo 4 days ago

Tbh I agree the final light steps can give it more of an arcade style and feel less realistic. I think if I used this effect widely I’d make a more professional light rays image with a different opacity. I’m not entirely sure I’d use the subsurface effects or not. If I did I may reduce their intensity.

One challenge with a demo like this is that subtle effects may look better, but they are harder to see. So I balanced making them visible enough to appreciate which is more intense than I’d otherwise want.

prisenco 4 days ago

Very cool stuff!

Little tip, you might want to add a -webkit-user-select: none on the container elements so it prevents highlighting in Safari when dragging the cursor out and back over.

tyleo 4 days ago

Thanks for the tip! I had `user-select: none` on the container which had the glass inside of it but apparently that didn't work. I just updated the page with `user-select: none` on the glass itself and it looks like that did the job.

prisenco 4 days ago

I may be mistaken, but I think you have to use the -webkit- prefix for it to work in Safari.

It's working great in Chrome and Firefox though.

tyleo 4 days ago

Interesting, I'll add the prefix and install desktop Safari in the future.

I was testing on Safari iOS and it looks like the non-prefix version worked there.

chrismorgan 4 days ago

https://developer.mozilla.org/en-US/docs/Web/CSS/user-select... says Safari and Safari for iOS are still -webkit- only, and I’m pretty sure that stuff gets checked and updated automatically.

prisenco 4 days ago

Yeah it's basically fine in Safari because mobile requires a double tap to highlight, but only a single click on desktop. So highlighting is more intentional on mobile than desktop.

yett 4 days ago

Safari is only available on macOS

Terretta 4 days ago

You must mean something by this. What do you mean?

// Written from a browser Apple calls Safari on iOS.

yett 3 days ago

I assumed that by them saying "installing desktop Safari" they are using an OS other than macOS since Safari comes preinstalled on macOS and can't even be uninstalled at least not without disabling System Integrity Protection.

hipadev23 3 days ago

How does the dragging itself work? custom js?

tyleo 3 days ago

In the Cross-Platform Dynamic Light section I describe `data-*` attributes (https://www.tyleo.com/html-glass.html#cross-platform-dynamic...).

The dragging works with another bit of JavaScript--the only other bit on the page--which uses a `data-click-drag-area` to define an element which will contain draggable children and a `data-click-drag-item` attribute to indicate a child can be dragged.

The the parent must be a 'positioned element' (it must have `position` set to something in CSS) the children must have `position: absolute`.

I did this in TypeScript. I'll share the code below. You have to call `initDataClickDrag` from another script... if you want to include this script directly you can just remove the `export` keyword and call `initDataClickDrag()` at the bottom after it is defined:

  export const initDataClickDrag = () => {
    // Get all of the areas we can drag items in
    const dragAreas = document.querySelectorAll("[data-click-drag-area]");
    for (const dragArea of dragAreas) {
      // Only iterate `HTMLElement`s
      if (!(dragArea instanceof HTMLElement)) continue;

      // Get all of the items we can drag
      const dragItems = dragArea.querySelectorAll("[data-click-drag-item]");
      for (const dragItem of dragItems) {
        // Only iterate `HTMLElement`s
        if (!(dragItem instanceof HTMLElement)) continue;

        let isDragging = false;
        let lastCursorX: number | undefined = undefined;
        let lastCursorY: number | undefined = undefined;

        // Mouse down event to start dragging
        const downCallback = (obj: {
          readonly pageX: number;
          readonly pageY: number;
        }) => {
          isDragging = true;
          lastCursorX = obj.pageX;
          lastCursorY = obj.pageY;
        };

        dragItem.addEventListener("mousedown", (e) => {
          downCallback(e);
        });

        dragItem.addEventListener("touchstart", (e) => {
          const touches = e.touches;
          if (touches.length === 0) return;
          downCallback(touches[0]);
        });

        // Mouse move event to scroll while dragging
        const moveCallback = (obj: {
          readonly pageX: number;
          readonly pageY: number;
        }): boolean => {
          if (!isDragging) return false;

          if (lastCursorX === undefined) return false;
          if (lastCursorY === undefined) return false;

          const x = lastCursorX - obj.pageX;
          const y = lastCursorY - obj.pageY;

          const left = dragItem.offsetLeft - x;
          const top = dragItem.offsetTop - y;

          dragItem.style.left = `${left.toString()}px`;
          dragItem.style.top = `${top.toString()}px`;

          // Get dragArea dimensions
          const dragAreaRect = dragArea.getBoundingClientRect();

          // Get element dimensions
          const elementRect = dragItem.getBoundingClientRect();

          if (dragItem.offsetLeft < 0) dragItem.style.left = "0px";
          if (dragItem.offsetTop < 0) dragItem.style.top = "0px";

          if (left + elementRect.width > dragAreaRect.width) {
            // Right boundary
            const left = dragAreaRect.width - elementRect.width;
            dragItem.style.left = `${left.toString()}px`;
          }

          if (top + elementRect.height > dragAreaRect.height) {
            // Bottom boundary
            const top = dragAreaRect.height - elementRect.height;
            dragItem.style.top = `${top.toString()}px`;
          }

          lastCursorX = obj.pageX;
          lastCursorY = obj.pageY;

          return true;
        };

        document.addEventListener("mousemove", (e) => {
          moveCallback(e);
        });

        document.addEventListener(
          "touchmove",
          (e) => {
            const touches = e.touches;
            if (touches.length === 0) return;
            if (!moveCallback(touches[0])) return;
            e.preventDefault();
          },
          { passive: false },
        );

        // Mouse up event to stop dragging
        document.addEventListener("mouseup", () => {
          isDragging = false;
        });

        document.addEventListener("touchend", () => {
          isDragging = false;
        });
      }
    }
  };