This one reminds me of this cool card effect
---
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.
> 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.
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.
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.
It's working great in Chrome and Firefox though.
I was testing on Safari iOS and it looks like the non-prefix version worked there.
// Written from a browser Apple calls Safari on iOS.
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;
});
}
}
};