La etiqueta <pre> sirve para crear texto preformateado. Esto quiere decir simplemente que los caracteres de salto de línea, espacios y tabulaciones son tenidos en cuenta. HTML no Los tendría en cuenta de manera predeterminada.
Lo que pasa es que las etiquetas html siguen siendo etiquetas. Por mucho que utilices la etiqueta <pre>, lo que colocas dentro es código HTML. Por eso te lo procesa y renderiza las etiquetas. El caso del código CSS es diferente porque no tiene etiquetas, por eso te lo muestra bien en una etiqueta <pre>.
La solución pasa por utilizar los caracteres especiales para los símbolos mayor y menor que. Me refiero a < para el símbolo < y > para el símbolo >. Así consigues que las etiquetas de tu código no sean realmente etiquetas y no se muestren como tales. Sino que serán texto con símbolos que el navegador no entenderá como etiquetas.
Otro detalle importante, que no es necesario de manera obligatoria, pero sí recomendable sobre todo desde el punto de vista de la semántica, es que uses la etiqueta <code> dentro de la etiqueta <pre>. Como digo, <code> no es necesario para que el texto se convierta en código, sino para que se muestre como tal en la página. El navegador usará el estilo que tenga asignado para el código, pero sobre todo otros sistemas como robots que lean la página podrán saber que ese bloque de texto es un bloque de código.
Para que lo tengas más claro te pongo un ejemplo sobre cómo podría quedar más o menos un bloque de código HTML, para que se vea correctamente en un documento HTML al mostrarse en el navegador.
<pre><code><ul>
<li>Item 1 <b>Se muestra el código</b></li>
<li>Item 2 <i>HTML como código</i></li>
<li>Item 3</li>
</ul></code></pre>