Button component with customizable background image - html

I'm building a reusable Button component in React whose background image is an image of a button (e.g. PNG export from Figma) and is customizable.
My current implementation is as follows -
Button.js
const Button = ({ url }) => {
const inlineStyle = {
backgroundImage: `url(${url})`
}
return (
<button
type='submit'
style={inlineStyle}
className='button'
/>
)
}
Button.css
.button {
width: 100%;
padding: 30px 0;
border: none;
background-color: rgba(0, 0, 0, 0);
background-position: 50% 50%;
background-size: contain;
background-repeat: no-repeat;
cursor: pointer;
}
But, the problem is this doesn't support all kind of button sizes. If a button is really wide and short in height, I'll have to readjust it again with another className like so -
const Button = ({ url, modifierClass }) => {
const inlineStyle = {
backgroundImage: `url(${url})`
}
return (
<button
type='submit'
style={inlineStyle}
className={`button ${modifierClass}`}
/>
)
}
.modifier {
max-width: 300px;
}
If it is a narrow button -
.modifier {
max-width: 140px;
}
My question is how do I code the Button component and its CSS so that it supports all kind of background image button shape; narrow, wide or tall on a fixed size?

I think I got what are you looking for. Change background-size: 'contain' to background-size: 'cover' and give it max-width: 100px (100px for example) and by using padding, height, min-height or etc., you get the same result and background doesn't need to be readjusted.
.ButtonComponent {
width: 100%;
max-width: 100px; /* Max width for the button */
padding: 30px 0; /* `min-height` / `height` */
border: none;
background-color: rgba(0, 0, 0, 0);
background-position: 50% 50%;
background-size: cover; /* Change `contain` to `cover` */
background-repeat: no-repeat;
cursor: pointer;
transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
}

With Ali Bahaari's help I figured out I can use max-width to control the Button's size.
I can accept size as prop and put it as max-width in inline style,
const Button = ({ url, size }) => {
const inlineStyle = {
backgroundImage: `url(${url})`,
maxWidth: size
}
return (
<button
type='submit'
style={inlineStyle}
className='button'
/>
)
}
and use the component like <Button size='100px' />.
This won't support responsiveness as inline style doesn't support media queries. If adjusting size per screen is needed, I'll just have to use the way in my question.

Related

CSS not working in Safari, but in Chrome and other browsers it does

The homepage of my project has a gallery of images that scroll every x seconds automatically.
On Chrome and Firefox everything is fine, but on Safari only the first image shows well and the others are blank slides.
Here the HomePage component:
import { useEffect, useState, useRef } from 'react'
import './home.styles.scss'
const galleriaDiImmagini = [
'https://i.ibb.co/LCzz4P4/1.webp',
'https://i.ibb.co/txwnt76/2.webp',
'https://i.ibb.co/XCHDFpx/3.webp',
'https://i.ibb.co/S6F1rtc/4.webp',
'https://i.ibb.co/P5GwHPz/5.webp'
]
const delay = 6000
const HomePage = () => {
const [index, setIndex] = useState(0)
const timeoutRef = useRef(null)
const resetTimeout = () => timeoutRef.current ? clearTimeout(timeoutRef.current) : null
useEffect(() => {
resetTimeout()
timeoutRef.current = setTimeout(
() =>
setIndex(prevIndex =>
prevIndex === galleriaDiImmagini.length - 1 ? 0 : prevIndex + 1
),
delay
)
return () => {
resetTimeout()
}
}, [index])
return (
<div className='homepage'>
<div
className='slide-container'
style={{ transform: `translate3d(${-index * 100}%, 0, 0)` }}
>
{
galleriaDiImmagini.map((img, i) => (
<div
key={ i }
className='slide'
style={{
'background': `url(${img}) no-repeat center center fixed`
}}
>
</div>
))
}
</div>
<div className="punti-container">
{galleriaDiImmagini.map((_, i) => (
<div
key={i}
className={`punto${index === i ? " active" : ""}`}
onClick={() => {
setIndex(i);
}}
>
</div>
))}
</div>
</div>
)
}
export default HomePage
And the styles:
$colore-tosto: #2FA7CF;
.homepage {
position: relative;
overflow: hidden;
height: 100vh;
.slide-container {
height: 100%;
width: 100%;
position: relative;
white-space: nowrap;
-webkit-transition: transform 1000ms ease-in-out;
-moz-transition: transform 1000ms ease-in-out;
-o-transition: transform 1000ms ease-in-out;
transition: transform 1000ms ease-in-out;
.slide {
display: inline-block;
height: 100%;
width: 100%;
background-size: contain;
-webkit-background-size: contain;
-moz-background-size: contain;
-o-background-size: contain;
}
}
.punti-container {
width: 100%;
text-align: center;
position: absolute;
top: 75%;
.punto {
display: inline-block;
height: 20px;
width: 20px;
border-radius: 50%;
background-color: rgba($color: #ffff, $alpha: 0.5);
border: 2.5px solid $colore-tosto;
margin: 15px;
&:hover {
cursor: pointer;
background-color: rgba($color: #ffff, $alpha: 0.9);
}
&.active {
background-color: white;
}
}
}
#media only screen and (max-width: 730px) {
.punti-container {
top: 80%;
.punto {
height: 17px;
width: 17px;
border-width: 1.5px;
margin: 10px;
}
}
.slide-container {
.slide {
background-size: auto 100% !important;
}
}
}
}
And here a live video of the site.
I thank in advance anyone who tries to give me a hand.
You need to remove background-attachment : fixed not supported on the safari , check it here Can I use , last parameter of background css key is an attachment
Problem
Seems like safari has a bug and creates problem when using transition: all or transition: xSeconds. It may sometimes crash.
Solution
Change it to transition: color 1000ms ease-in-out; (Or any other property. Just dont keep all ).
Read more here: My website crashes Safari (both desktop and iOS) consistently
I would check:
if you have any extensions that may be limiting your code's behavior in Safari:
did you inspect your code in Safari and check if the CSS is being imported
Split your CSS code in chunks, and start adding each chunk gradually, and check the browser to see if its working; if it suddenly stops working then that's the faulty chunk. You then need to see if there are any incompatibilities with a property or something like that

How to give hover effect to specific part of image?

For a project, I am trying to hover background colour change effect to specific part of image. Suppose I have this image
Now I want that when I hover over the orange on the right side I the background glow should change. Similarly I can do it for the other items in the image.
I could not find any property where I can specify coordinates of the image where hover effect can be applied to.
Is there any way this is possible? Any pre processing through photoshop or some software that might help?
edit: by background glow I mean using drop-shadow(16px 16px 20px red);property
I've made you an example with just the right-most orange, but you get the idea. just place SVGs and give each a unique class name (for size/position).
You can use an online tool, such as this, to create your SVG shapes.
A thing to keep in mind is if the image resizes, the position & size of the highlights should remain correct (this is why working with percentages is best)
.imageWrapper {
width: 500px;
position: relative;
}
.imageWrapper img {
width:100%; height:100%;
object-fit: contain;
}
.image-area {
position: absolute;
top: 69.5%; /* position should be in percentages */
left: 73.5%; /* position should be in percentages */
transition: .4s;
mix-blend-mode: lighten; /* work the best with the default black fill of svg shapes */
cursor: pointer;
}
.image-area:hover {
filter: drop-shadow(0 0 20px gold);
}
.image-area--orange-1 {
/* sizes should be in percentages */
width: 21%;
height: 18%;
}
<div class='imageWrapper'>
<!-- fill with SVG areas -->
<svg viewBox="0 0 100 100" class='image-area image-area--orange-1'>
<circle cx="50" cy="50" r="50"/>
</svg>
<!-- -->
<img src="https://i.stack.imgur.com/8BVo6.jpg"/>
</div>
Please consider using the image region mapping, this should be standard for most browser and don't need image manipulation
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/map
const circleClip = document.querySelector("#bg");
function removeIntro() {
circleClip.classList.remove("intro");
}
function circleMove(e) {
removeIntro();
circleClip.style.setProperty("--x", e.clientX + "px");
circleClip.style.setProperty("--y", e.clientY + "px");
}
document.addEventListener("mousemove", circleMove);
circleClip.addEventListener("touchmove", (e) => {
removeIntro();
let touch = e.touches[0];
e.preventDefault();
circleClip.style.setProperty("--x", touch.clientX + "px");
circleClip.style.setProperty("--y", touch.clientY + "px");
});
:root {
--x: 0px;
--y: 0px;
}
body {
position: relative;
margin: 0;
overflow: hidden;
background-image: url(https://i.stack.imgur.com/8BVo6.jpg);
background-size: 100% 35%;
backdrop-filter: grayscale(100%);
}
#bg {
position: relative;
background: url(https://i.stack.imgur.com/8BVo6.jpg) no-repeat;
background-size: 100% 35%;
min-height: 300vh;
clip-path: circle(10% at var(--x) var(--y));
}
#bg.intro {
clip-path: circle(100% at 50% 50%);
animation: circleIntro 1800ms cubic-bezier(0.645, 0.045, 0.355, 1) both;
}
#keyframes circleIntro {
100% {
clip-path: circle(10% at 50% 50%);
}
}
<div id="bg" class="intro"></div>

Use IntersectionObserver with rootMargin to change when reaching 50% viewport height

I'm completely flummoxed by the rootMargin property of intersection observer.
My goal is to add a class to an element when half it's height has crossed the vertical center of the viewport.
In my current project, nothing I do seems to impact the "root intersection rectangle" and the class is always added immediately. I've tested in latest Chrome and Firefox.
Here's the reduced test case:
// https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API
const options = {
root: null, // default, use viewport
rootMargin: '0px 0px -50% 0px',
threshold: 0.5 // half of item height
}
const circle = document.getElementById('circle');
const observerCallback = function(entries, observer) {
console.log('intersected');
circle.classList.add('intersected');
}
window.addEventListener('load', function(event) {
const observer = new IntersectionObserver(observerCallback, options);
observer.observe(circle);
}, false);
.circle {
margin: 100vh auto;
width: 200px;
height: 200px;
background-color: tomato;
border-radius: 50%;
transition: background-color 2s ease-in-out;
}
.circle.intersected {
background-color: mediumseagreen;
}
<div class="circle" id="circle"></div>
I am quite perplexed by IntersectionObserver myself sometimes, but referring to this post, it was a lot easier to grasp for me.
What was probably giving you trouble was checking for if it actually was intersecting or not. So I added an if-statement along with the property isIntersecting that is found on IntersectionObserver entries.
I also added a check for IntersectionObserver if it is available on the client and removed root: null from the options as it should default to the viewport anyway.
If you only use this IntersectionObserver for adding a class once, don't forget to observer.unobserve(circle) or observer.disconnect() when it isn't needed anymore to prevent memory leaks.
// https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API
const options = {
rootMargin: '0px 0px -50% 0px',
threshold: 0.5 // half of item height
}
const circle = document.getElementById('circle');
const observer = new IntersectionObserver(entries => {
const [{ isIntersecting }] = entries
if (isIntersecting) {
console.log('intersected');
circle.classList.add('intersected');
} else {
console.log('not-intersecting');
}
}, options);
window.addEventListener('load', () => {
if ('IntersectionObserver' in window) observer.observe(circle);
}, false);
.circle {
margin: 100vh auto;
width: 200px;
height: 200px;
background-color: tomato;
border-radius: 50%;
transition: background-color 2s ease-in-out;
}
.circle.intersected {
background-color: mediumseagreen;
}
<div class="circle" id="circle"></div>

Fading between sprite images without calling out image url in CSS

Is it possible to fade between sprite images on hover without calling out the image URL in the CSS?
I have a ton of two-image sprite sheets. I want each one to switch on hover, but the CSS will be far too bloated if I have to create a new element for every one with a "background: url(x)".
.frame {
height: 500px;
width: 500px;
overflow: hidden;
}
.sprite {
background: url(image.png) 0px 0px no-repeat;
position: relative;
height: 500px;
width: 500px;
}
.sprite::after {
content: "";
background: url(image.png) -500px 0px no-repeat;
opacity: 0;
transition: opacity 0.35s;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
.sprite:hover::after {
opacity: 1;
transition: opacity: 0.35s;
}
I'd rather call them out here:
<div class="sprite frame">
</div>
Here's a JSFiddle of the effect I want, but I want to call out the image URL in the HTML, so I don't have 100 different CSS elements calling out different images.
Hmm. If you are open to a jQuery solution this may help. You can use a "data alt-src" HTML attribute to store two image URLs within one tag, then call a jQuery .hover() function on that class.
HTML:
<img class="xyz" data-alt src="http://cdn1.iconfinder.com/data/icons/fatcow/32/accept.png" src="http://cdn1.iconfinder.com/data/icons/fatcow/32/cancel.png" />
jQuery:
var sourceSwap = function () {
var $this = $(this);
var newSource = $this.data('alt-src');
$this.data('alt-src', $this.attr('src'));
$this.attr('src', newSource);
}
$(function () {
$('img.xyz').hover(sourceSwap, sourceSwap);
});
http://jsfiddle.net/LmVRZ/2/
I realize this demo doesn't include the opacity transition but I think it could be built in.

CSS3 transform on hover

I'm currently trying to do a transform (rotateY) on an A element, that have already been animated in CSS.
To sum up : the first animation happens when the user scroll to this block, I had a "show" class, which launch a css animation. This works nice. On a second time, when the .show element is hover, I make him rotate. That second animation, as you can see, is buggy. It flickers, and don't work as expected.
Here's the CSS (Sass / Compass) part :
li
width: 23%
float: left
background: top center url('../img/bgHexa.png') no-repeat
&+li
margin-left: 2.5%
& > a
background: url('../img/pictosHome.png') 65px 60px no-repeat
width: 100%
display: block
height: 220px
opacity: 0
position: relative
top: 90px
+transform(rotateY(135deg))
+transition(all 0.6s ease-in-out)
&.show
top: 0
opacity: 1
+transform(rotateY(0))
&.hover
+transform(rotateY(180deg))
The JS part :
if( app.toTop >= $('#prezComp ul').offset().top- app.height/2){
var nb = 0;
var int = setInterval( function(){
var el = $('#prezComp .picto').eq(nb);
el.addClass('show');
nb++;
if( nb > 4) {
clearInterval(int);
}
}, 300);
}
I tried many HTML hacks, I tried with JS, it doesn't work better...
What am I doing wrong ?
Mouse hover is not happing at the time ,Try this code:
CSS:
#prezComp li { //style.css:558
width: 23%;
float: left;
position: relative;
z-index: 100000;
background: top center url("../img/bgHexa.png") no-repeat;
}