Pace bowler Dale Steyn grabbed 5-50 to help restrict Sri Lanka to 283-9 in reply to South Africa's first-innings score of 455-9 declared on day three Friday of the first test.
It was the 23rd five-wicket haul in 73 tests for Steyn, who gained appreciable reverse swing to rip through the middle order after taking two wickets in the first session.
Steyn took 3-8 in a five-over spell in the post-tea session as Sri Lanka slipped from 190-4 to 201-7 and stared at the prospect of following on.
Steyn was spot on during that five-over spell as Lahiru Thirimanne (38) was caught behind, Dinesh Chandimal (8) pulled straight to short mid-wicket, and Dilruwan Perera (0) inside-edged one to be caught behind.
At the close, Rangana Herath was not out on 12 with last man Shaminda Eranga due to come in as Sri Lanka was still 172 runs behind.
Captain Angelo Mathews produced a fine counter-offensive to save home blushes with another staunch innings of 89 which came with the help of one six and 14 fours.
Mathews once smashed Morne Morkel for five fours in an over to get past the 50-run mark for the 15th time in his career. He added 71 for the eighth wicket with Herath in what was the best partnership of the innings.
The Sri Lanka middle order had been struggling to capitalize on opener Upul Tharanga's innings of 83 before Mathews stepped up yet again. In sight of a century in a third straight test, Mathews mistimed a sweep of Imran Tahir and sent the ball onto his stumps. He'd played 182 deliveries, and gave legspinner Tahir his only wicket.
Tharanga, 20 in an overnight score of 30 without loss, marked his return to test cricket after more than six years with his fourth half century
The left-hander struck one six and 14 fours before he was stumped off spinner Jean-Paul Duminy after facing 155 balls. His was the only wicket to fall in the middle session.
Earlier, Steyn and Morkel shared three wickets in the morning session to peg back the home side.
Tharanga batted with confidence well past lunch, adding 59 runs for the second wicket with Kumar Sangakkara, who was out for 24.
Tharanga passed 50 with two fours off an over from Duminy. He struck the offspinner past point and two deliveries later, straight down the ground, to move from 45 to 53.
But Sri Lanka was always under pressure from the early dismissals of Kaushal Silva and the experienced pair of Sangakkara and Mahela Jayawardene in the first session.
Silva was caught by Vernon Philander at the long-leg boundary without adding to his overnight score of 8 as he tried to hook Steyn.
Sangakkara was out immediately after a brief 10-minute rain break as he shaped for a pull off Morkel but played the ball on to the stumps.
Jayawardene, who has announced he will retire from tests next month, was trapped lbw by Steyn after making just 3.