Reparar webkit móvil 100vh

El manejo de 100vh de Mobile Webkit podría necesitar más atención

Tengo problemas

Mientras disfrutaba de esta aplicación web mía, siempre me di cuenta de que el contenido de la parte superior del pliegue en el safari móvil estaba algo roto. Si vuelve a cargar la página, verá que el primer contenido cargado para cada publicación de blog contiene los siguientes elementos:

  • Título de la publicación
  • Subtítulo con una breve descripción
  • Pie de página con autor, categoría y fecha

Este pie de página está destinado a estar siempre visible en la parte inferior de la ventana gráfica al cargar la página, imitando una vista de portada similar a una revista. Se ve bien, comunica un mensaje claro (es decir, de qué se trata esta publicación) y evita cualquier desviación. Todo este héroe se implementa de una manera sencilla usando la altura del valor CSS: 100vh, usando toda la altura disponible de la pantalla del dispositivo.

Cuando el usuario se desplaza un poco hacia abajo, esta portada de héroe se desvanece dejando lugar para el contenido real. Sin embargo, lo curioso es que esto nunca funcionó en iOS. Así es como se veía:

Image f1a30a30a13c

Como puede ver, no se ve ningún pie de página. De hecho, está escondido debajo de la barra inferior de Safari.

Tengo una solución

Después de ponerme mi sombrero de sherlock y comenzar una investigación en la red mundial, seguramente descubrí una solución, ya que otros han tenido este problema antes.

tl; dr: usar -webkit-fill-available de Webkit hace el truco. Así es como se ve:

.hero-container {
  min-height: 100vh;
  /* fix for mobile webkit */
  min-height: -webkit-fill-available;
}

Sin embargo, usar esto como plantilla para mi solución JSS no funcionó al 100%. La altura solo se redujo la altura intrínseca del contenedor. Por lo tanto, tengo que detectar en qué dispositivo estoy y aplicar condicionalmente el estilo correcto.

/**
 * Check if current session runs in
 * a mobile webkit instance.
 */
export function isMobileWebkit() {
  const ua = window.navigator.userAgent;
  const iOS = !!ua.match(/iPad/i) || !!ua.match(/iPhone/i);
  const webkit = !!ua.match(/WebKit/i);
  return iOS && webkit && !ua.match(/CriOS/i);
}

/**
 * As I'm using Next.js, window is undefined
 * on the server, therefore we need to call
 * 'isMobileWebkit' after mounting.
 */
export function useIsMobileWebkit() {
  const [flag, setFlag] = useState(false);

  useEffect(() => setFlag(isMobileWebkit()), []);

  return flag;
}

Lo que ahora queda es el uso condicional:

// We're using 'clsx' for classname merging.
import clsx from "clsx";

function Hero(){
  const classes = useStyles();
  
  return (
    <div
      className={clsx(classes.hero, {
        [classes.heroCssWebkitFix]: isMobileWebKit,
      })}>
      ...
      </div>
  );
}

// ... slice of the styling:

const useStyles = makeStyles(theme => ({
  hero: {
    minHeight: "100vh",
    width: "100%",
    display: "flex",
    flexDirection: "column",
    alignItems: "stretch",
    paddingBottom: theme.spacing(2),
    paddingTop: theme.spacing(2),
  },
  heroCssWebkitFix: {
    // mobile viewport bug fix
    minHeight: "-webkit-fill-available",
    paddingBottom: 0,
  },
}))

Aplicando la corrección anterior, todo funciona bien ahora. ¡Gracias por la lectura! Fuentes de las publicaciones originales mencionadas a continuación en el apéndice.

  • Tom