Tabla de contenido
Una vez más aquí. Hoy vengo a dejar unas líneas aquí en las que vamos a ver algo que un usuario me pidió hace como dos semanas. El caso es que este me decía que quería saber cómo podía crear su propio acortador de URL para utilizar en su web. Y bueno, después de buscar un poco por la red, encontré la forma de hacerlo, por lo que me dispongo a mostrar archivo a archivo cómo hacerlo.
Antes de nada, decir que un acortador de URL es una herramienta en línea que permite acortar una URL larga en una URL más corta y fácil de compartir. La URL corta, es esencialmente una versión abreviada de la URL original y suele tener menos caracteres.
Estos acortadores son útiles en situaciones donde se desea compartir una URL larga pero el espacio es limitado, como en las redes sociales o en los mensajes de texto. Al acortar la URL, se puede ahorrar espacio y hacer que sea más fácil de leer y compartir.
Además de acortar URLs, algunos acortadores de URL también pueden proporcionar estadísticas y análisis sobre el tráfico de la URL acortada. Aquí se pueden añadir el número de clics y la ubicación geográfica de los visitantes. Esto puede ser útil para los especialistas en marketing y los propietarios de sitios web que desean realizar un seguimiento del rendimiento de sus enlaces acortados.
El código del acortador de URL
Antes de nada, decir que este proyecto necesita de una base de datos. Esta se puede crear de forma sencilla en localhost o en un servidor web. En el repositorio de GitHub dejo el archivo .SQL para crear la base de datos, y donde se puede descargar todo el código que vamos a ver a continuación.
También quiero decir que el proyecto se divide en varios archivos, los cuales tendremos en diferentes carpetas. La estructura del proyecto que vamos a ver a continuación será la que se ve en la siguiente captura.

El archivo index.php
Este código es una página web HTML con un poco de código PHP y JavaScript. En el se va a mostrar el listado de enlaces acortados y el formulario necesario desde donde poner las URL largas.
?php require_once "php/config.php"; header("Cache-Control: no-cache, must-revalidate"); require_once "inc/redireccion.php"; ?> <!DOCTYPE html> <html lang="es"> <head> <meta charset="UTF-8"> <title>Acortador de URL con PHP</title> <link rel="icon" type="image/jpg" href="img/favicon.ico"/> <link rel="stylesheet" href="css/style.css"> <link rel="stylesheet" href="https://unicons.iconscout.com/release/v3.0.6/css/line.css"> </head> <body> <div class="wrapper"> <div class="titulo" title="made in entreunosyceros.net">Acortador de URL</div> <form action="#" autocomplete="off"> <input type="text" spellcheck="false" name="full_url" placeholder="Escribe la URL larga aquí (https://...)" required> <i class="url-icon uil uil-link"></i> <button>Acortar</button> </form> <?php require_once "inc/listado.php"; ?> </div> <div class="blur-effect"></div> <div class="popup-box"> <div class="info-box">Tu URL acortada está lista.</div> <form action="#" autocomplete="off"> <label>Edita tu URL corta</label> <input type="text" class="shorten-url" spellcheck="false" required> <i class="copy-icon uil uil-copy-alt"></i> <button>Guardar</button> </form> </div> <script src="script.js"></script> </body> </html>
En la primera línea, se incluye un archivo de configuración de PHP que contiene información de configuración de la base de datos y otros valores importantes para el sitio web. Luego, se establece una cabecera para controlar el caché del navegador, lo que significa que el navegador no debe almacenar en caché los datos de la página.
Después de eso, se incluyen varios archivos CSS para formatear y diseñar la página web. A continuación veremos un formulario en la página que permite a los usuarios ingresar una URL larga y acortarla. Al hacer clic en el botón «Acortar«, se llama a la lógica para acortar la URL y mostrar el resultado al usuario.
Finalmente, encontraremos una ventana emergente en la página que permite a los usuarios editar la URL corta que se ha generado. Al hacer clic en el botón «Guardar«, se guarda la URL corta en la base de datos del sitio web y se muestra al usuario.
El archivo script.js
Este archivo lo guardaremos en la misma carpeta que el archivo index.php. En este se encontrará el código para acortar las URL largas y obtener una URL corta, más fácil de recordar y de compartir.
El código de este archivo es una implementación de un acortador de URL que utiliza JavaScript y PHP. La función principal del código es tomar una URL larga y generar una URL corta que redirige al usuario a la URL original. El código utiliza una combinación de eventos de JavaScript y solicitudes de XMLHttpRequest para realizar esta tarea.
En este archivo es donde es necesario modificar la línea // Pega aquí tu dominio con el dominio que quieras utilizar (el mismo que necesitas poner en el archivo config.php).
const form = document.querySelector(".wrapper form"); const fullURL = form.querySelector("input"); const shortenBtn = form.querySelector("form button"); const blurEffect = document.querySelector(".blur-effect"); const popupBox = document.querySelector(".popup-box"); const infoBox = popupBox.querySelector(".info-box"); const form2 = popupBox.querySelector("form"); const shortenURL = popupBox.querySelector("form .shorten-url"); const copyIcon = popupBox.querySelector("form .copy-icon"); const saveBtn = popupBox.querySelector("button"); form.addEventListener("submit", e => { e.preventDefault(); }); shortenBtn.addEventListener("click", () => { const xhr = new XMLHttpRequest(); xhr.open("POST", "php/url-controll.php", true); xhr.addEventListener("load", () => { if (xhr.readyState === 4 && xhr.status === 200) { const data = xhr.response; if (data.length <= 5) { blurEffect.style.display = "block"; popupBox.classList.add("show"); // Pega aquí tu dominio: entreunosyceros.net/ const domain = "localhost/url/"; shortenURL.value = domain + data; copyIcon.addEventListener("click", () => { shortenURL.select(); document.execCommand("copy"); }); saveBtn.addEventListener("click", () => { form2.addEventListener("submit", e => { e.preventDefault(); }); const xhr2 = new XMLHttpRequest(); xhr2.open("POST", "php/save-url.php", true); xhr2.addEventListener("load", () => { if (xhr2.readyState === 4 && xhr2.status === 200) { const data = xhr2.response; if (data === "success") { location.reload(); } else { infoBox.classList.add("error"); infoBox.innerText = data; } } }); const shorten_url1 = shortenURL.value; const hidden_url = data; xhr2.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); xhr2.send(`shorten_url=${shorten_url1}&hidden_url=${hidden_url}`); }); } else { alert(data); } } }); const formData = new FormData(form); xhr.send(formData); }); //copiar URL al portapapeles const clipboardBtns = document.querySelectorAll('.copy-button'); clipboardBtns.forEach(btn => { btn.addEventListener('click', () => { const urlShort = btn.getAttribute('data-clipboard-text'); navigator.clipboard.writeText(urlShort); // Cambiar el texto del botón a "Copiado" y añadir la clase "copied" btn.textContent = 'Copiado'; btn.classList.add('copied'); // Después de 3 segundos, cambiar el texto del botón a "Copiar" de nuevo y quitar la clase "copied" setTimeout(() => { btn.textContent = 'Copiar'; btn.classList.remove('copied'); }, 3000); }); }); // Borrar enlace // Asocia el evento click al elemento padre document.addEventListener('click', function(event) { // Comprueba si el elemento que se ha hecho clic tiene el ID 'borrar-enlace' if (event.target.id === 'borrar-enlace') { event.preventDefault(); // Evita que se ejecute el enlace var request = new XMLHttpRequest(); request.open('GET', event.target.href, true); request.onload = function() { if (request.status >= 200 && request.status < 400) { location.reload(); // Actualiza la página } }; request.send(); } }); // Abrir en nueva pestaña y actualizar const shortenUrls = document.querySelectorAll('.shorten-url'); shortenUrls.forEach(url => { url.addEventListener('click', e => { // Prevenir el comportamiento por defecto del enlace e.preventDefault(); // Abrir el enlace en una nueva pestaña window.open(url.href, '_blank'); // Esperar 1 segundo antes de recargar la página setTimeout(() => { location.reload(); }, 1000); }); });
Primero, el código selecciona varios elementos HTML mediante el uso de los selectores de clase y de etiqueta, como el formulario de entrada, los botones, la caja emergente y los iconos. Luego, el código agrega eventos a algunos de estos elementos, como el botón de acortamiento, el botón de copiado y el botón de guardado. Estos eventos activan la creación de una solicitud XMLHttpRequest que se envía a un archivo PHP en el servidor, que maneja la generación de una URL corta y su almacenamiento en una base de datos.
El código también incluye funciones que se encargan de copiar la URL corta al portapapeles del usuario, y de borrar la URL corta de la base de datos en caso de que el usuario lo desee. También se incluye una función que abre la URL corta en una nueva pestaña y actualiza la página después de un segundo.
Archivo config.php
Los siguientes archivos que vamos a ver, este incluido, los vamos a guardar dentro de la carpeta php. Este código PHP se encarga de establecer una conexión con una base de datos MySQL.
<?php /* si estás trabajando en localhost, dentro del directorio url, entonces no necesita cambiar nada. Pero si estás pensando en subirlo al servidor en vivo, entonces tiene que editar más cosas 1. Pega la URL de tu sitio web con una barra diagonal (/) en la variable de dominio, no necesitas escribir https://wwww. antes del nombre de dominio si tienes configurada la redirección de dominio 2. Cambia los valores de usuario, password, db en consecuencia mencionados en los comentarios a continuación 3. Dirigete al archivo JavaScript y busca "Pega aquí tu dominio". Ahí pega tu URL en la constante domain 4. Después de todos los cambios, debes esperar porque los cambios guardados en el archivo javascript pueden tardar en reflejarse */ $domain = "localhost/url/"; // entreunosyceros.net/ $host = "localhost"; $user = "XXXXXX"; // Nombre de usuario de la base de datos $pass = "XXXXXX"; // Password de la base de datos $db = "acortador"; // Nombre de la base de datos $conn = mysqli_connect($host, $user, $pass, $db); if(!$conn){ echo "Error de conexión a la base de datos: ".mysqli_connect_error(); } ?>
Aquí se establecen variables para el dominio del acortador de URL, el nombre de usuario, la contraseña y el nombre de la base de datos, utilizados para la conexión con la base de datos. Después, se establece una conexión a la base de datos utilizando la función mysqli_connect() y se verifica si la conexión se ha establecido correctamente. Si no se ha establecido correctamente, se imprime un mensaje de error.
En resumen, este código PHP establece una conexión con una base de datos MySQL utilizada en este sistema de acortador de URL.
Archivo delete.php
Este código es un script PHP que maneja la eliminación de registros en la base de datos. En resumen, lo que hace es recibir el valor de una variable ‘id’ o ‘delete‘ desde una solicitud GET. Después utiliza esos valores para eliminar registros específicos en la tabla ‘url‘ de la base de datos.
<?php require_once "config.php"; header("Cache-Control: no-cache, must-revalidate"); $deleted = false; if(isset($_GET['id'])){ $delete_id = mysqli_real_escape_string($conn, $_GET['id']); $stmt = $conn->prepare("DELETE FROM url WHERE shorten_url = ?"); $stmt->bind_param("s", $delete_id); $stmt->execute(); $stmt->close(); $deleted = true; } elseif(isset($_GET['delete']) && $_GET['delete'] == "all"){ $stmt = $conn->prepare("DELETE FROM url"); $stmt->execute(); $stmt->close(); $deleted = true; } if($deleted){ header("Refresh:0; url=../"); } else { echo "No se encontraron registros para eliminar"; } ?>
Primero, se requiere un archivo de configuración ‘config.php‘, que contiene información sobre la conexión a la base de datos. Luego, se establece una cabecera HTTP que indica que la respuesta no debe ser almacenada en caché.
A continuación, se verifica si la variable ‘id‘ está presente en la solicitud GET. Si lo está, se escapa su valor para evitar inyecciones SQL y se prepara una consulta de eliminación SQL. Esta utiliza el valor de la variable ‘id’ para seleccionar el registro correspondiente en la tabla ‘url‘. Luego, la consulta se ejecuta y se cierra. Si la eliminación tiene éxito, se establece una bandera ‘deleted‘ en verdadero.
Si la variable ‘delete‘ está presente en la solicitud GET y su valor es «all«, se ejecuta una consulta de eliminación SQL que elimina todos los registros en la tabla ‘url‘. Si la eliminación tiene éxito, se establece la bandera ‘deleted‘ en verdadero.
Finalmente, si se ha eliminado algún registro, se redirige al usuario a la página de inicio utilizando la cabecera HTTP ‘Refresh‘. Si no se han eliminado registros, se muestra un mensaje de error.
El archivo save-url.php
Este código es un script de PHP que se encarga de procesar los datos enviados por un formulario para actualizar una URL corta.
<?php include "config.php"; $og_url = $_POST['shorten_url']; $shorten_url = str_replace(' ', '', $og_url); $hidden_url = $_POST['hidden_url']; if(isset($shorten_url) && !empty($shorten_url)){ if(preg_match("/\//i", $shorten_url)){ $explodeURL = explode('/', $shorten_url); $shortURL = end($explodeURL); if($shortURL != ""){ $stmt = $conn->prepare("SELECT shorten_url FROM url WHERE shorten_url = ? AND shorten_url != ?"); $stmt->bind_param("ss", $shortURL, $hidden_url); $stmt->execute(); $stmt->bind_result($result); $stmt->fetch(); $stmt->close(); if(!$result){ $stmt2 = $conn->prepare("UPDATE url SET shorten_url = ? WHERE shorten_url = ?"); $stmt2->bind_param("ss", $shortURL, $hidden_url); $stmt2->execute(); $stmt2->close(); echo "success"; }else{ echo "La URL corta que has escrito ya existe. Introduce otra!"; } }else{ echo "Requerido!! - Es necesario escribir una URL corta!"; } }else{ echo "URL inválida - No puedes editar el nombre de dominio!"; } }else{ echo "Requerido!! - Es necesario escribir una URL corta!"; } ?>
Primero, el script incluye el archivo «config.php« que contiene la conexión a la base de datos.
Luego, el script obtiene los datos del formulario enviados por POST. Se incluye la URL corta original, la URL corta actual (oculta) y la nueva URL corta que se desea actualizar.
A continuación, se comprueba si se ha proporcionado una URL corta y no está vacía.
Si se ha proporcionado una URL corta, se comprueba si la URL corta actualizada ya existe en la base de datos. Para hacer esto, el script consulta la base de datos para ver si hay una URL corta existente que coincida con la URL corta actualizada, pero que no sea la URL corta actual (oculta) que se está actualizando. Si se encuentra una coincidencia, se devuelve un mensaje de error.
Si no se encuentra una coincidencia, el script actualiza la URL corta actual (oculta) en la base de datos con la nueva URL corta proporcionada.
Finalmente, se devuelve un mensaje de éxito o de error según el resultado de la operación de actualización.
El archivo url-controll
Este código PHP recibe una URL completa mediante una solicitud POST. Esta se procesa para crear una URL corta única y guardarla en una base de datos.
<?php include "config.php"; $full_url = $_POST['full_url']; if(!empty($full_url) && filter_var($full_url, FILTER_VALIDATE_URL)){ $ran_url = substr(md5(microtime()), rand(0, 26), 5); $stmt = $conn->prepare("SELECT shorten_url FROM url WHERE shorten_url = ?"); $stmt->bind_param("s", $ran_url); $stmt->execute(); $stmt->bind_result($shorten_url); $stmt->fetch(); if(empty($shorten_url)){ $stmt->close(); $stmt2 = $conn->prepare("INSERT INTO url (full_url, shorten_url, clicks) VALUES (?, ?, '0')"); $stmt2->bind_param("ss", $full_url, $ran_url); if($stmt2->execute()){ $stmt2->close(); echo $ran_url; }else{ echo "Algo ha salido mal. Vuelve a intentarlo!"; } }else{ echo "Algo ha salido mal. Vuelve a regenerar la URL!"; } }else{ echo "$full_url - Esto no es una URL válida (es necesario utilizar https:...!"; } ?>
Este código PHP toma una URL completa proporcionada mediante un método POST. Verifica que sea válida utilizando la función «filter_var» de PHP y, si es válida, genera una URL corta aleatoria utilizando una combinación de la función «md5«, «microtime» y «rand«. Luego, comprueba si la URL corta generada ya existe en la base de datos. Si no existe, inserta la URL completa y la URL corta en la tabla «url» de la base de datos. Después devuelve la URL corta generada como respuesta.
Si la URL corta generada ya existe en la base de datos, devuelve un mensaje de error. Si la URL completa proporcionada no es válida, devuelve un mensaje de error. El código utiliza la función «prepare» de PHP para preparar y ejecutar consultas SQL seguras, utilizando parámetros vinculados para evitar la inyección SQL.
El archivo listado.php
Este archivo se va a guardar dentro de la carpeta inc. El código primero realiza una consulta SQL para obtener todos los registros de la tabla url
ordenados por el id de forma descendente.
<?php $sql2 = mysqli_query($conn, "SELECT * FROM url ORDER BY id DESC"); if (mysqli_num_rows($sql2) > 0) { $sql = mysqli_prepare($conn, "SELECT COUNT(*), SUM(clicks) FROM url"); mysqli_stmt_execute($sql); mysqli_stmt_bind_result($sql, $count, $total); mysqli_stmt_fetch($sql); ?> <div class="statistics"> <span>Links Totales: <span><?php echo $count ?></span> - Clics Totales: <span><?php echo $total ?></span></span> <a href="php/delete.php?delete=all" onclick="return confirm('¿Estás seguro de que deseas borrar todos los registros?')" title="Borrar todos los registros">Borrar Todo</a> </div> <div class="urls-area"> <div class="title"> <li>URL corta</li> <li>URL original</li> <li>Clics</li> <li>Acción</li> <li>Eliminar</li> </div> <?php while ($row = mysqli_fetch_assoc($sql2)): ?> <div class="data"> <li> <a href="<?php echo $row['shorten_url'] ?>" class="shorten-url" target="_blank"> <?php echo $domain . (strlen($row['shorten_url']) > 50 ? substr($row['shorten_url'], 0, 50) . '...' : $row['shorten_url']) ?> </a> </li> <li> <?php echo strlen($row['full_url']) > 50 ? substr($row['full_url'], 0, 50) . '...' : $row['full_url'] ?> </li> <li><?php echo $row['clicks'] ?></li> <li><button class="copy-button" data-clipboard-text="<?php echo $domain . $row['shorten_url'] ?>" title="Copiar enlace">Copiar</button></li> <li><a href="php/delete.php?id=<?php echo $row['shorten_url'] ?>" title="Borrar enlace" id="borrar-enlace">Borrar</a></li> </div> <?php endwhile; ?> </div> <?php } ?>
Luego, si hay registros en la tabla url
, se realiza otra consulta SQL para contar el número total de registros y sumar todos los clics. Esta información se muestra en una sección «Estadísticas».
A continuación, se muestra una tabla que contiene los detalles de cada registro de URL corta y original, la cantidad de clics y dos botones: «Copiar» y «Borrar«. El botón «Copiar» copia la URL corta al portapapeles y el botón «Borrar» elimina el registro correspondiente de la base de datos. Además también se añade el enlace para borrar todos los registros de la base de datos.
El archivo redireccion.php
Este archivo se guarda también en la carpeta inc. Este código es un script de redireccionamiento. Su propósito es redirigir al usuario a una URL específica cuando se accede a una URL acortada generada previamente por el script de acortamiento de URL que vimos anteriormente. La idea es que cuando un usuario hace clic en una URL corta, el script busca en la base de datos de URL cortas la URL original correspondiente y la redirige al usuario a esa dirección.
<?php $new_url = ""; if(isset($_GET)){ foreach($_GET as $key=>$val){ $u = mysqli_real_escape_string($conn, $key); $new_url = str_replace('/', '', $u); } $sql = mysqli_query($conn, "SELECT full_url FROM url WHERE shorten_url = '{$new_url}'"); if(mysqli_num_rows($sql) > 0){ $sql2 = mysqli_query($conn, "UPDATE url SET clicks = clicks + 1 WHERE shorten_url = '{$new_url}'"); if($sql2){ $full_url = mysqli_fetch_assoc($sql); header("Location:".$full_url['full_url']); } } } ?>
Primero, el código inicializa la variable $new_url
como una cadena vacía. Luego, se comprueba si hay alguna variable GET en la URL mediante el uso de isset($_GET)
.
Si hay una variable GET, el script recorre cada una de las variables GET y para cada una de ellas, escapa los caracteres especiales y los caracteres que podrían usarse para acceder a otra página utilizando la función mysqli_real_escape_string()
. Después, la función str_replace()
se utiliza para eliminar cualquier barra diagonal del valor obtenido.
A continuación, se realiza una consulta SQL para recuperar la URL original correspondiente a la URL acortada.
Si la consulta SQL devuelve al menos una fila, se actualiza el número de clics en la base de datos mediante una consulta SQL de actualización. Finalmente, se redirige al usuario a la URL original utilizando la función header()
de PHP.
Archivo style.css
Este archivo se va a guardar dentro de la carpeta css. Aquí será donde añadiremos las reglas CSS para dar estilo a nuestro proyecto.
*{ margin: 0; padding: 0; box-sizing: border-box; text-decoration: none; font-family: 'Comic Sans MS', sans-serif; } body{ display: flex; align-items: center; justify-content: center; min-height: 100vh; background: linear-gradient(to bottom, #d38afd, #6100bb); padding: 0 10px; } .wrapper{ background: #fff; padding: 20px; width: 900px; border-radius: 5px; border: 5px solid #000; } ::selection{ background: rgba(120, 23, 184, 0.3); } .wrapper form{ height: 50px; width: 100%; display: flex; position: relative; align-items: center; } form .url-icon{ position: absolute; width: 50px; text-align: center; font-size: 23px; color: #c4c4c4; pointer-events: none; } form input:valid ~ .url-icon{ color: #710db4; } form input{ height: 100%; width: 100%; outline: none; padding: 0 120px 0 45px; font-size: 20px; caret-color: #9520b2; border: 2px solid #ddd; border-radius: 5px; transition: all 0.1s ease; } form input:valid{ border-color: #4c20b2; } form input::placeholder{ color: #c4c4c4; } form input:focus::placeholder{ color: #d9d9d9; } form button{ position: absolute; right: 6px; padding: 5px 15px; font-size: 18px; border-radius: 5px; border: none; outline: none; background: #5820b2; color: #fff; cursor: pointer; transition: all 0.3s ease; } form button:hover{ background: #6000fc; } .wrapper form button{ opacity: 0; pointer-events: none; } .wrapper form input:valid ~ button{ opacity: 1; pointer-events: auto; } .wrapper a{ color: #000; } .wrapper .statistics{ margin: 20px 0; display: flex; padding-right: 5px; align-items: center; justify-content: space-between; } .statistics span{ font-size: 17px; } .statistics span span{ font-weight: 500; } .statistics a:hover{ color: #20B2AA; } .wrapper .urls-area{ border: 1px solid #ddd; border-radius: 5px; margin-bottom: 5px; max-height: 400px; overflow-y: scroll; } .urls-area::-webkit-scrollbar{ width: 0px; } .urls-area :is(.title, .data){ display: flex; width: 100%; justify-content: space-between; } .urls-area li{ width: 100%; list-style: none; border-right: 1px solid #ddd; } .urls-area li:last-child{ border-right: 0px; } .urls-area .title li{ text-align: center; background: #f2f2f2; padding: 10px 0; } .urls-area .data li{ padding: 8px 10px; display: flex; align-items: center; justify-content: center; word-break: break-all; } .urls-area .data li:last-child a{ height: 100%; width: 100%; display: flex; align-items: center; justify-content: center; } .urls-area .data li a:hover{ color: #20B2AA; text-decoration: underline; } .urls-area .data li:last-child a:hover{ text-decoration: none; } .urls-area .data:nth-child(odd){ background: #f2f2f2; } .urls-area li:nth-child(1){ max-width: 37%; } .urls-area li:nth-child(2){ max-width: 32%; } .urls-area li:nth-child(3){ max-width: 8%; } .urls-area li:nth-child(4){ max-width: 11%; } .data > li:nth-child(4) > a{ font-size: 10px; text-decoration: none; } .urls-area li:nth-child(5){ max-width: 12%; } .urls-area li:nth-child(5) > a{ font-size: 10px; } .blur-effect{ position: absolute; top: 0; left: 0; height: 100%; width: 100%; backdrop-filter: blur(2px); background: rgba(0,0,0,0.01); display: none; } .popup-box{ position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%) scale(0.9); background: #fff; padding: 25px; max-width: 480px; width: 100%; border-radius: 5px; box-shadow: 0px 5px 10px rgba(0,0,0,0.1); opacity: 0; visibility: hidden; pointer-events: none; transition: all 0.3s ease; } .popup-box.show{ opacity: 1; pointer-events: auto; visibility: visible; transform: translate(-50%, -50%) scale(1); } .popup-box .info-box{ color: #280f57; background: #bef4f1; border: 1px solid #7de8e3; padding: 10px; text-align: center; font-size: 17px; border-radius: 5px; } .popup-box .info-box.error{ color: #721c24; background: #f8d7da; border-color: #f5c6cb; } .popup-box form{ margin-top: 10px; position: relative; } .popup-box form label{ font-size: 18px; } .popup-box form .copy-icon{ position: absolute; right: 10px; top: 50%; transform: translateY(-100%); font-size: 20px; cursor: pointer; } .copy-icon:hover{ color: #20B2AA; } .popup-box form input{ height: 45px; padding: 0 35px 0 15px; margin-top: 3px; border: 1px solid #ccc; } .popup-box form input:focus{ border-color: #20B2AA; } .popup-box form button{ width: 100%; height: 45px; position: relative; right: 0; font-size: 20px; margin-top: 10px; } /*Título*/ .titulo { box-sizing: border-box; padding: 10px; border: none; font: normal 38px / normal "Warnes", Helvetica, sans-serif; color: rgba(255, 255, 255, 1); text-align: center; white-space: pre; text-shadow: 0 0 10px rgb(255, 0, 0), 0 0 20px rgba(255, 0, 0), 0 0 30px rgba(255, 0, 0), 0 0 40px #727272, 0 0 70px #727272, 0 0 80px #4e4e4e, 0 0 100px #2b2b2b; -webkit-transition: all 200ms cubic-bezier(0.42, 0, 0.58, 1); -moz-transition: all 200ms cubic-bezier(0.42, 0, 0.58, 1); -o-transition: all 200ms cubic-bezier(0.42, 0, 0.58, 1); transition: all 200ms cubic-bezier(0.42, 0, 0.58, 1); width: 100%; margin-top: 10px; margin-bottom: 10px; } .titulo:hover { text-shadow: 0 0 10px rgb(69, 0, 109), 0 0 20px rgb(73, 0, 95), 0 0 30px rgb(57, 0, 68), 0 0 40px #580054, 0 0 70px #000000, 0 0 80px #000000, 0 0 100px #000000; } /*Botón copiar*/ .copy-button { background-color: #a200ff; /* Color de fondo verde */ color: white; /* Texto en blanco */ border: none; /* Sin borde */ padding: 10px 10px; /* Espacio de relleno */ text-align: center; /* Alineación del texto */ text-decoration: none; /* Sin subrayado */ display: inline-block; /* Mostrar como un elemento de línea */ font-size: 12px; /* Tamaño de fuente */ margin: 10px 0; /* Margen superior e inferior */ cursor: pointer; /* Cambiar el cursor al pasar sobre el botón */ border-radius: 15%; max-width: 100%; max-height: 100%; } @media (max-width: 768px) { .copy-button { padding: 3px 3px; font-size: 10px; max-width: 100%; max-height: 100%; } } /*Botón copiado*/ .copied { background-color: #2c0074; } /* Después de 3 segundos, eliminar la clase "copied" */ .copied.remove:after { content: 'Copiar'; background-color: #a200ff; }
Y bueno, para terminar, faltaría el favicon.ico que se va a guardar en la carpeta img. Tras todo esto, solo queda guardar todo esto en nuestro servidor y ponerlo a funcionar.

Descarga el proyecto desde GitHub
Todo este proyecto, se puede descargar desde el repositorio en GitHub al que lo he subido. Si prefieres ver como funciona en un servidor web, puedes dirigirte al subdominio en el que lo alojé.
Tengo que decirle al usuario que me preguntó por este proyecto, que lo suyo es ponerle un login de usuarios. Por que de no hacerlo, cualquier usuario podrá eliminar los enlaces de los demás. Además de poner enlaces que quizás no te interese que estén presentes en tu acortador. Esto es algo que quizás vaya implementando en este proyecto, si tengo tiempo.