Anrich Nortje claimed career-best figures of 6/56 as Sri Lanka were rolled for 157 on the first day in Johannesburg, before 92* from Dean Elgar helped South Africa to a strong position by the close.
Kusal Perera helped get the visitors off to a respectable start after opting to bat first, scoring a 67-ball 60 to take Sri Lanka to 71/1.
The one early wicket to fall was that of captain Dimuth Karunaratne, who was undone by vicious bouncer from Nortje with the ball looping off the glove to give Quinton de Kock the first of five catches in the innings.
But when Perera edged Wiaan Mulder to Aiden Markram at slip shortly before lunch, it sparked a collapse from which Sri Lanka couldn't fully recover. Mulder produced a peach to dismiss Kusal Mendis for a duck later in the same over, and then snared Lahiru Thirimanne in his next over, with both players also caught in the slips as the all-rounder surged to figures of 3/1 before the break.
Nortje then joined in on the action, finding steep bounce from back of a length to take the edge of debutant Minod Bhanuka as he looked to punch it off the back foot.
Sri Lanka had collapsed from 71/1 to 84/5 by lunch, and things didn't much improve after the break. Niroshan Dickwella was well caught by De Kock in front of first slip to give Nortje his third, and Dasun Shanaka feathered one to be dismissed by the same combination and leave the visitors 110/7.
Wanindu Hasaranga wrested back some momentum with a whirlwind 29, putting on 39 runs for the eighth wicket with Dushmantha Chameera. But Lutho Sipamla drew a thin edge from Hasaranga to give De Kock his fourth catch and Chameera edged an attempted pull shot off Nortje to give the gloveman his fifth catch, and Nortje his second Test five-for.
Nortje went full and straight to make a mess of Asitha Fernando's stumps and wrap up the innings for 157.
South Africa negotiated their way to tea without loss, with Dean Elgar becoming the 10th player to score 4000 Test runs for South Africa just before the break.
Markram fell a short time into the evening session, edging Asitha to slip to hand the 23-year-old seamer his maiden Test wicket, but Rassie van der Dussen held firm with Elgar and by stumps the pair's unbroken partnership was worth 114 runs as the hosts closed on 148/1, trailing by just nine runs.