<!DOCTYPE html>
<html lang="ru">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Плавное расширение карточки через Grid</title>
    <style>
        * {
            box-sizing: border-box;
        }

        body {
            font-family: Arial, sans-serif;
            padding: 20px;
            margin: 0;
        }

        .grid-container {
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
            gap: 16px;
            transition: all 0.5s ease-out;
            position: relative;
        }

        .card {
            background-color: #f0f0f0;
            padding: 20px;
            position: relative;
            transition: background-color 0.5s ease, transform 0.3s ease;
            cursor: pointer;
            z-index: 1;
            border-radius: 8px;
            box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
            overflow: hidden;
            display: flex;
            flex-direction: column;
            justify-content: space-between;
            /* Добавляем переходы для размеров и позиций */
            transition: width 0.5s ease, height 0.5s ease, top 0.5s ease, left 0.5s ease, opacity 0.5s ease;
        }

        .card .expand-btn {
            margin-top: 10px;
            padding: 8px 12px;
            background-color: #007bff;
            color: white;
            border: none;
            cursor: pointer;
            transition: background-color 0.3s ease;
            border-radius: 4px;
        }

        .card .expand-btn:hover {
            background-color: #0056b3;
        }

        .card.expanded {
            background-color: #d0eaff;
            z-index: 1000;
            box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
            position: absolute;
            /* Начальные стили устанавливаются через JavaScript */
        }

        .card.hidden {
            opacity: 0.5;
            pointer-events: none;
            transition: opacity 0.3s ease;
        }

        /* Добавим плавные переходы для деталей */
        .card .details {
            max-height: 0;
            opacity: 0;
            transition: max-height 0.5s ease, opacity 0.5s ease;
            overflow: hidden;
        }

        .card.expanded .details {
            max-height: 500px; /* Достаточно большое значение */
            opacity: 1;
            margin-top: 10px;
        }
    </style>
</head>

<body>
    <div class="grid-container">
        <div class="card">
            <div class="content">Карточка 1</div>
            <div class="details" style="display:none;">
                <p>Дополнительная информация для карточки 1...</p>
            </div>
            <button class="expand-btn">Развернуть</button>
        </div>
        <div class="card">
            <div class="content">Карточка 2</div>
            <div class="details" style="display:none;">
                <p>Дополнительная информация для карточки 2...</p>
            </div>
            <button class="expand-btn">Развернуть</button>
        </div>
        <!-- По аналогии еще 18 карточек -->
    </div>

    <script>
        const gridContainer = document.querySelector('.grid-container');

        let expandedCard = null;
        let originalStyles = {};

        document.querySelectorAll('.expand-btn').forEach(button => {
            button.addEventListener('click', (e) => {
                e.stopPropagation();
                const card = button.closest('.card');

                if (card.classList.contains('expanded')) {
                    // Свернуть карточку
                    collapseCard(card);
                } else {
                    // Свернуть любую ранее развернутую карточку
                    const currentlyExpanded = gridContainer.querySelector('.card.expanded');
                    if (currentlyExpanded) {
                        collapseCard(currentlyExpanded);
                    }

                    // Развернуть текущую карточку
                    expandCard(card);
                }
            });
        });

        // Свернуть карточку
        function collapseCard(card) {
            if (!card.classList.contains('expanded')) return;

            // Скрыть детали
            const details = card.querySelector('.details');
            if (details) {
                details.style.display = 'none';
            }

            // Вернуть стили
            card.style.position = originalStyles.position;
            card.style.top = originalStyles.top;
            card.style.left = originalStyles.left;
            card.style.width = originalStyles.width;
            card.style.height = originalStyles.height;
            card.style.zIndex = originalStyles.zIndex;

            // Убрать класс expanded
            card.classList.remove('expanded');

            // Показать все карточки
            document.querySelectorAll('.card').forEach(c => {
                if (c !== card) {
                    c.classList.remove('hidden');
                }
            });

            expandedCard = null;
        }

        // Развернуть карточку
        function expandCard(card) {
            // Сохранить исходные стили
            originalStyles.position = card.style.position;
            originalStyles.top = card.style.top;
            originalStyles.left = card.style.left;
            originalStyles.width = card.style.width;
            originalStyles.height = card.style.height;
            originalStyles.zIndex = card.style.zIndex;

            // Получить текущие координаты и размеры карточки
            const rect = card.getBoundingClientRect();
            const gridRect = gridContainer.getBoundingClientRect();

            // Установить абсолютные позиции на основе текущего положения
            card.style.position = 'absolute';
            card.style.top = `${rect.top - gridRect.top + gridContainer.scrollTop}px`;
            card.style.left = `${rect.left - gridRect.left + gridContainer.scrollLeft}px`;
            card.style.width = `${rect.width}px`;
            //card.style.height = `${rect.height}px`;
            card.style.zIndex = '1000';

            // Форсируем перерисовку для применения начальных стилей
            card.getBoundingClientRect();

            // Добавляем класс expanded
            card.classList.add('expanded');

            // Показать детали
            const details = card.querySelector('.details');
            if (details) {
                details.style.display = 'block';
            }

            // Получить новые размеры для анимации
            const newWidth = gridRect.width - (rect.left - gridRect.left);
            const newHeight = card.scrollHeight + 20; // Дополнительное пространство для padding

            // Установить конечные параметры для анимации
            card.style.top = `${rect.top - gridRect.top + gridContainer.scrollTop}px`;
            card.style.left = `0px`;
            card.style.width = `100%`;
            card.style.height = `${newHeight}px`;

            // Скрыть другие карточки в той же строке
            const columns = getColumnCount();
            const rowIndex = getRowIndex(card, columns);
            const cards = Array.from(gridContainer.children);
            const start = rowIndex * columns;
            const end = start + columns;
            cards.slice(start, end).forEach(c => {
                if (c !== card) {
                    c.classList.add('hidden');
                }
            });

            expandedCard = card;
        }

        // Функция для определения количества колонок в сетке
        function getColumnCount() {
            const gridStyles = window.getComputedStyle(gridContainer);
            const gridTemplateColumns = gridStyles.getPropertyValue('grid-template-columns');
            return gridTemplateColumns.split(' ').length;
        }

        // Функция для получения индекса строки, в которой находится карточка
        function getRowIndex(card, columns) {
            const cards = Array.from(gridContainer.children);
            const cardIndex = cards.indexOf(card);
            return Math.floor(cardIndex / columns);
        }

        // Обработка изменения размера окна
        window.addEventListener('resize', () => {
            const expandedCard = gridContainer.querySelector('.card.expanded');
            if (expandedCard) {
                collapseCard(expandedCard);
            }
        });
    </script>
</body>

</html>


У меня возникла нестандартная идея, которая, на мой взгляд, может быть реализована. Для того чтобы избежать нарушения сеточной вёрстки, при нажатии на кнопку на карточке необходимо создать копию этой свёрнутой карточки, которая будет располагаться в том же месте, что и оригинал. Копия должна сразу разворачиваться. При сворачивании этой копии она должна плавно сворачиваться до состояния оригинальной карточки и затем удаляться.