MatrixForm with zeroes formatted in gray

SeedRandom[0]

(m = RandomInteger[{0, 2}, {4, 4}]) // MatrixForm

enter image description here

Note that by using parentheses, the definition of m does not include the wrapper MatrixForm

matrixForm[x_List] := ((x /. 0 :> Style[0, LightGray]) // MatrixForm)

m // matrixForm

enter image description here

The wrappers, MatrixForm or matrixForm are only for display. If you want to use the matrix, use m. Any subsequent operation is done on m and the display is formatted using the wrapper.

(m2 = Transpose@m) // matrixForm

enter image description here

m == (m2 // Transpose)

(* True *)

To get the % behavior you want, you need to create a wrapper, that is, a function that does not evaluate, but creates a nice format when it is rendered. The usual way to do this is to use MakeBoxes. Here is a version that should target only matrix entries, and also avoid evaluation leaks:

matrixForm /: MakeBoxes[matrixForm[x_List], StandardForm] := Replace[
    Replace[Hold[x], 0->Style[0, Red], {3}],
    Hold[z_] :> MakeBoxes[MatrixForm[z]]
]

Note that I used Red instead of LightGray to improve visualization for this answer. Now that matrixForm is a wrapper, you can add it to $OutputForms as suggested in (51898).

Unprotect[$OutputForms];
AppendTo[$OutputForms, matrixForm];
Protect[$OutputForms];

Example:

matrixForm[{{f[0], 0}, {1, 0}}]

enter image description here

And using %:

%

{{f[0], 0}, {1, 0}}

For SparseArray objects, you could add the following format:

matrixForm /: MakeBoxes[matrixForm @ SparseArray[a_, b_, back_, c__], StandardForm] := With[
    {new = SparseArray[a, b, Style[back, Red], c]},
    MakeBoxes[
        MatrixForm @ new,
        StandardForm
    ]
]

Example:

matrixForm[IdentityMatrix[3, SparseArray]]

enter image description here

Check %:

%

SparseArray[Automatic, {3, 3}, 0, { 1, {{0, 1, 2, 3}, {{1}, {2}, {3}}}, {1, 1, 1}}]