Search code examples
javascriptreactjstypescripttailwind-css

React and Tailwind Positioning an Item on Hover within DOM


I have an icon that shows only when I hover over a container. It's currently position relative because I need it to stay within the container. With it being relative though it pushes down the rest of the items within the container; however, I want them to stay vertically centered. I know I could use negative margin to force them back up, but that is typically seen as bad practice so I'm trying to figure out another way to handle this.


enter image description here

^^ What the front end looks like with the position relative, the icon stays within the white container.


enter image description here

^^ What the front end looks like with the position absolute, no matter where I position the icon its gonna be on the same spot on the screen regardless of which container I hover over.


This is the react code for the scrolling list of containers

return (
        <div className="container mx-auto p-4">
            <h1 className="text-4xl font-bold text-center mb-6 text-amber-100">Task List</h1>
            <div className="overflow-x-auto  scrollbar-custom">
                <ul className="flex space-x-4 mb-10 blue" style={{ listStyleType: 'none', padding: 0 }}>
                    { tasks.map((task) =>
                        (
                            <li key={task.id} 
                                className="TaskBox bg-white shadow-lg shadow-amber-200/60 rounded-lg p-8 shadow-sm min-w-[300px] max-w-[300px] flex-shrink-0">
                                <div className="deleteTask">
                                    <DeleteTask task={task}/>
                                </div>
                                <div className="TaskInfo">
                                <h4 className="text-2xl font-semibold text-gray-800 mb-2">{task.title}</h4>
                                <p  className="text-sm text-gray-500 mb-2"
                                    >Created on: {new Date(task.createdAt).toLocaleDateString('en-US', {
                                    year: 'numeric',
                                    month: 'long',
                                    day: 'numeric'
                                })}</p>
                                <p className="text-gray-600 mb-4">{task.description}</p>
                                <p  className={`text-sm ${task.isCompleted ? 'text-green-500' : 'text-red-500'}`}>Completed: {task.isCompleted ? 'Yes' : 'No'}</p>
                                </div>
                            </li>
                        ))}
                </ul>
            </div>
        </div>
    );

CSS for the code

.trashIcon
{
  width: 1.75em;
  height: 1.95em;
  color: indianred;
  transition: transform 0.15s ease;
}

.deleteTask:hover .trashIcon
{
  transform: scale(1.1);
  color: firebrick;
}

.deleteTask
{
   position: absolute !important;
   left: 50%;
   bottom: 12%;
   padding: 6px 6px;
   background-color: lightslategray;
   border-radius: 12px;
   transition: transform 0.15s ease;
   visibility: hidden;
}
.deleteTask:hover
{
  background-color: dimgrey;
}

.TaskBox:hover .deleteTask
{
   display: inline-block;
   transform: scale(1.1);
   visibility: visible;
}

.TaskInfo
{
  display: flex-column;
  justify-content: center;
  align-items: center;
}

.newTaskForm
{
  width: 50%;
}

I tried switching to position absolute as it doesn't effect other DOM elements, I was expecting it to stay within the white container though, but it only stays within the container like shown in the first picture with position relative.


Solution

  • You need to add "relative" as a className to your card, so the absolute className has something to refer to for its placement.

    This should work:

    return (
            <div className="container mx-auto p-4">
                <h1 className="text-4xl font-bold text-center mb-6 text-amber-100">Task List</h1>
                <div className="overflow-x-auto  scrollbar-custom">
                    <ul className="flex space-x-4 mb-10 blue" style={{ listStyleType: 'none', padding: 0 }}>
                        { tasks.map((task) =>
                            (
                                <li key={task.id} 
                                    className="relative TaskBox bg-white shadow-lg shadow-amber-200/60 rounded-lg p-8 shadow-sm min-w-[300px] max-w-[300px] flex-shrink-0">
                                    <div className="deleteTask">
                                        <DeleteTask task={task}/>
                                    </div>
                                    <div className="TaskInfo">
                                    <h4 className="text-2xl font-semibold text-gray-800 mb-2">{task.title}</h4>
                                    <p  className="text-sm text-gray-500 mb-2"
                                        >Created on: {new Date(task.createdAt).toLocaleDateString('en-US', {
                                        year: 'numeric',
                                        month: 'long',
                                        day: 'numeric'
                                    })}</p>
                                    <p className="text-gray-600 mb-4">{task.description}</p>
                                    <p  className={`text-sm ${task.isCompleted ? 'text-green-500' : 'text-red-500'}`}>Completed: {task.isCompleted ? 'Yes' : 'No'}</p>
                                    </div>
                                </li>
                            ))}
                    </ul>
                </div>
            </div>
        );